首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Dubbo跨机房调用

Dubbo跨机房调用

作者头像
叔牙
修改2022-04-06 14:28:55
3.9K3
修改2022-04-06 14:28:55
举报

微信公众号:PersistentCoder

一、背景

在一些跨境业务中,特别是电商或者SAAS场景,用户群体是分离的,经营者在国内,而产品使用者在海外,或者外海用户分布在多个大区,而数据中心在其中一个大区,那么就会存在一些跨大区或者跨机房的服务调用场景,比如海外用户分布在亚洲大区和美洲大区,亚洲大区与美洲大区之间数据节点部署是MS架构,那么如果美洲大区的用户有写操作,就需要调用亚洲大区的服务。

那么就需要在双机房部署的时候,优先调用本机房服务,然后如果本机房没有服务或者不符合要求,那么会调用其他机房的服务。

二、实现方案

多注册中心

dubbo2.7.5版本引入了多注册中心集群负载均衡能力支持,对于多注册中心订阅的场景,选址时的多了一层注册中心集群间的负载均衡:

在Cluster Invoker这一级,支持以下选址策略:

  • 指定优先级
<!-- 来自 preferred=“true” 注册中心的地址将被优先选择,只有该中心无可用地址时才 Fallback 到其他注册中心 -->
<dubbo:registry address="nacos://${nacos.address1}" preferred="true" />
  • 同zone优先
<!-- 选址时会和流量中的 zone key 做匹配,流量会优先派发到相同 zone 的地址 -->
<dubbo:registry address="nacos://${nacos.address1}" zone="asia" />
  • 权重轮询
<!-- 来自asia和america集群的地址,将以 8:2 的比例来分配流量 -->
<dubbo:registry id="asia" address="nacos://${nacos.address1}" weight=”80“ />
<dubbo:registry id="america" address="nacos://${nacos.address2}" weight=”20“ />
  • 默认,任意可用

配置调整

对于亚洲大区,读写都只需要调用本机房的服务,只需配置:

<dubbo:registry address="nacos://${asia.address}" preferred="true" />

对于美洲大区,需要调用亚洲大区的写服务,因此需要配置美洲和亚洲两个注册中心,并将美洲的注册中心标记为默认:

<dubbo:registry id="america" address="nacos://${america.address}" preferred="true" />
<dubbo:registry id="asia" address="nacos://${asia.address}" />

代码实现

@Reference(registry="asia")
WriteAPI writeApi;

写服务注入强制指定亚洲大区,这样对于美洲大区调用写服务会调用到亚洲大区,对于亚洲大区调用写服务也会调用本大区服务。

三、原理分析

多注册中心集群负载均衡能力支持主要由ZoneAwareClusterInvoker来实现,核心代码如下:

@Override
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    //首先优先选择标记为preferred的注册中心的ClusterInvoker
    for (Invoker<T> invoker : invokers) {
        ClusterInvoker<T> clusterInvoker = (ClusterInvoker<T>) invoker;
        if (clusterInvoker.isAvailable() && clusterInvoker.getRegistryUrl()
                .getParameter(REGISTRY_KEY + "." + PREFERRED_KEY, false)) {
            return clusterInvoker.invoke(invocation);
        }
    }
    //然后选择同zone的服务ClusterInvoker
    String zone = invocation.getAttachment(REGISTRY_ZONE);
    if (StringUtils.isNotEmpty(zone)) {
        for (Invoker<T> invoker : invokers) {
            ClusterInvoker<T> clusterInvoker = (ClusterInvoker<T>) invoker;
            if (clusterInvoker.isAvailable() && zone.equals(clusterInvoker.getRegistryUrl().getParameter(REGISTRY_KEY + "." + ZONE_KEY))) {
                return clusterInvoker.invoke(invocation);
            }
        }
        String force = invocation.getAttachment(REGISTRY_ZONE_FORCE);
        if (StringUtils.isNotEmpty(force) && "true".equalsIgnoreCase(force)) {
            throw new IllegalStateException("");
        }
    }

    //按照权重选择Invoker
    Invoker<T> balancedInvoker = select(loadbalance, invocation, invokers, null);
    if (balancedInvoker.isAvailable()) {
        return balancedInvoker.invoke(invocation);
    }

    //随机选择一个可用的
    for (Invoker<T> invoker : invokers) {
        ClusterInvoker<T> clusterInvoker = (ClusterInvoker<T>) invoker;
        if (clusterInvoker.isAvailable()) {
            return clusterInvoker.invoke(invocation);
        }
    }
    //随机选择一个
    return invokers.get(0).invoke(invocation);
}

从代码中我们可以看出几种策略的优先级,优先选择注册中心标记为preferred的Invoker调用,如果没有则选择同大区的服务调用,否则使用负载均衡根据权重选择Invoker,再者就随机选择一个可用的Invoker,最后如果前边都不满足则随便选择一个Invoker调用。

四、Bug

我们团队使用的dubbo框架版本是2.7.8,但是这个版本有一个比较低级的bug,就是前边代码中优先选择preferred的时候有一行代码获取preferred:

clusterInvoker.getRegistryUrl().getParameter(REGISTRY_KEY + "." + PREFERRED_KEY, false)

这里用REGISTRY_KEY和PREFERRED_KEY进行了连接,getParameter的key是registry.preferred,而注册中心配置的key是preferred,所以配置会不生效,后续高版本对该bug进行了修复:

但是升级版本是一个高风险的操作,我们不能拿别人的错误惩罚自己。当然我们还有别的解决方案,既然他要的key是registry.preferred那么我就给你这个key。

把registry.preferred=true拼到注册中心url的后边就行了,实测可用。配置改成如下:

<dubbo:registry id="america" address="nacos://${america.address}?registry.preferred=true" />
<dubbo:registry id="asia" address="nacos://${asia.address}" />

当然在高版本中直接使用preferred=true替换即可,在不确定当前版本是否有bug的情况下可以两种方式都写上。

五、总结

ZoneAwareClusterInvoker是其他ClusterInvoker的包装,ClusterInvoker又是Invoker的包装,那么ZoneAwareClusterInvoker的调用逻辑就是:

回过头来我们思考一个问题,就本篇文章分析的亚洲和美洲双大区注册中心场景中,美洲机房显式配置了两个注册中心,但是对于美洲集群,配置亚洲注册中心的目的只是订阅服务,没有双大区注册服务的诉求,然后dubbo的服务注册和订阅机制中并没有将注册和订阅做隔离,也就是说美洲的服务也会注册到亚洲注册中心,只不过不会有消费这而已,是不是在某种程度上造成了注册中心内存的浪费,以及美洲大区服务启动耗时增加。

本着浪费可耻,节约光荣的原则,那有没有一种机制或者有没有可能对于这种跨大区服务调用的场景,只有订阅服务诉求的情况下,做到服务订阅和服务注册隔离以及可个性化定制?

目前好像暂不支持,如果感兴趣可以自己研究下服务注册和订阅流程的源码,是否能够做到使用SPI或者其他方式做到隔离和定制化,以及实现之后的合理性和价值。

六、参考

ZoneAwareClusterInvoker

2.7.5新特性

多注册中心机制

[Dubbo-6034]

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PersistentCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景
  • 二、实现方案
    • 多注册中心
      • 配置调整
        • 代码实现
        • 三、原理分析
        • 四、Bug
        • 五、总结
        • 六、参考
        相关产品与服务
        微服务引擎 TSE
        微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档