背景介绍
- 注册中心 zookeeper (2C4G)*3
- 服务数 7000+,节点数 100000
- 线上ZK故障:突然有一个ZK节点CPU太高被打挂了,此时对该节点进行升配操作,然后ZK数据丢了,具体原因不详。此次故障导致几乎所有服务瘫痪
- 在接下来的一段时间里,还会新增大量的服务
这类故障其实已经出现好几次了,ZK问题还没找到具体原因(太菜了学习中),基于以上背景,想替换ZK注册中心。 其实也不是马上要替换ZK注册中心,替换ZK注册中心之前提过好多次,为此我们对比了一些常见的注册中心:etcd、nacos、zookeeper
- etcd和zookeeper都是基于CP模式;nacos支持AP/CP模式,如果用nacos作为Dubbo的注册中心,那自然就是用它的AP模式
- etcd和zookeeper虽然都是CP模式,但测试下来发现,在相同配置下,etcd表现更稳定
- 为了更方便的进行对比,前期日常/预发环境接入三套注册中心,对比效果
替换方案
在说替换方案之前,先简单讨论一下注册中心在Dubbo中的用途:提供者地址仓库。那么该注册中心有什么要求?
- 稳定性:该注册中心必须是稳定的,如果不稳定,提供者无法向注册中心注册自己,消费者无法从注册中心获取提供者地址,从而导致无法正常调用
- 一致性:如果注册中心各个节点上的数据不一致,此时不同消费者拿到的提供者列表不同:如果拿少了,有可能会导致提供者负载不均衡;如果拿多了,有可能会调用到已下线的节点
- 实时性:提供者节点发生变更,消费者需要能够及时感知
根据CAP理论,要不就AP,要不就CP,其实就分别对应上面的第1点和第2点。那对于Dubbo注册中心来说,哪个更重要一点呢?网上有一篇"阿里巴巴为什么不用ZooKeeper做服务发现",里面有做分析,大家可以看看。我也觉得稳定性更重要:
- 影响范围更小:以ZK为例,假如ZK长时间不可用,此时将导致所有的Dubbo临时节点下线,此时如果不在RPC框架层做一些降级措施,服务就无法正常调用了。而数据不一致可能只会影响到部分服务
- 默认情况下,基于Dubbo的Failover策略,可以降低数据不一致性的影响
上面简单介绍了注册中心在Dubbo中的用途,那该如何实现注册中心迁移呢?想到最简单的就是"多注册中心":
- 在现有注册中心的基础上,准备其它注册中心,如: zk、etcd、nacos
- 客户端:需要做一次升级,保证应用可以同时订阅和注册到多个注册中心
- 提供者:应用启动时候注册到多个中心,这样即可以被未升级的消费者消费,也可以被已升级的消费者消费
- 消费者:应用启动时订阅多注册中心,这样即可以消费未升级的提供者,也可以消费已升级的提供者
多注册中心
Dubbo本身已经支持多注册中心了,如果只是想接入多注册中心,只需要在客户端做相应的配置即可。但该如何让业务方更轻松的接入以及Dubbo多注册中心背后的原理是什么,这个是需要我们考虑的。
多注册中心接入方式
1、业务方直接接入,配置多个注册中心
@Bean
public RegistryConfig zkRegistryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("zookeeper");
registryConfig.setAddress("localhost:2181");
return registryConfig;
}
@Bean
public RegistryConfig nacosRegistryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setProtocol("nacos");
registryConfig.setAddress("localhost:8808");
return registryConfig;
}
// 或者在应用中配置
dubbo.registries.zk.protocol=zookeeper
dubbo.registries.zk.address=xxx
dubbo.registries.nacos.protocol=nacos
dubbo.registries.nacos.address=xxx
- 这种方式需要每个业务方都在应用中配置多个注册中心的信息,成本较大,推动比较困难
- 注册中心元信息分散在所有的客户端中,不方便管理。想象一下,假如后面我们想从 nacos切换到etcd该怎么办?有做一次这样的改动吗?
2、通过Dubbo的配置中心配置注册中心
dubbo.registries.zk.protocol=zookeeper
dubbo.registries.zk.address=xxx
dubbo.registries.nacos.protocol=nacos
dubbo.registries.nacos.address=xxx
- 在Dubbo管控台的全局配置中添加注册中心信息
- 这样子注册中心的信息就统一管理起来了,也避免了每个客户端都手动配置
3、内置注册中心
- 一般针对公司内部的版本,比如基于Dubbo二开
- 可以将注册中心相关信息放到apollo,在应用启动阶段从apollo加载相关信息并生成注册中心注入到Dubbo
每种方式都可以达到接入多注册中心的目的,具体看情况。
多注册中心原理解析
其实主要就涉及到一个核心问题:Dubbo是如何与注册中心进行交互的?即 提供者如何写?消费者如何读? 这个问题弄清楚了,多注册中心的原理也就自然懂了,见图:
- 多了一层 路由和负载均衡
- 每个注册中心可以看成是一个Invoker,可以通过前置路由找到对应的注册中心Invoker,然后根据前置负载均衡策略找到注册中心Invoker,然后再走Dubbo默认的路由复杂逻辑
- 在默认情况下,是没有注册中心路由和负载策略的,所以此时会选择固定(第一个)注册中心
AbstractClusterInvoker#invoke =>
ZoneAwareClusterInvoker#doInvoke =>
MockClusterInvoker#invoke =>
AbstractClusterInvoker#invoke =>
RegistryDirectory#doList =>
常见问题
多注册中心下的配置中心问题
- 默认情况下:如果应用中有多个注册中心,在不手动配置 配置中心的前提下,会自动生成多个配置中心,配置会被覆盖。在Dubbo2.7.8中,etcd无法使用默认配置中心,有bug(SPI etcd 和 etcd3 的问题),要解决
- 在多注册中心的情况下,要想不同时存在多个配置中心,想要手动配置一个配置中心,但这面临一个问题:需要业务方修改代码,即在应用中手动配置 配置中心。这种方式有个比较大的问题:不利于后期的配置中心迁移,比如现在用etcd,后面又想用nacos ; 接入成本也很大,除了升级客户端,还要改代码
- 可以将配置中心的信息放在Apollo,在Dubbo启动之前,完成配置中心的加载。这样即不需要客户端自己维护配置中心信息,又方便
多注册中心下的元数据中心问题
保持不变,元数据中心放在Dubbo配置中心上,也是统一管理
某个注册中心挂掉是否有影响
- 多注册就本身就是一种很好的降级方案
- 在多注册中心的情况下,如果某个注册中心挂了,相当于此时该注册中心Invoker被销毁了,此时前置路由自然会选者其它的注册中心
现有注册中心的下线问题
- 等到所有的应用都已经注册到了多个注册心,此时ZK就可以下线了
- 在默认情况下,所有客户端都在代码中配置了ZK为注册中心,此时ZK Server直接下线,客户端会收到报错:一些重试任务的报错
- 是否在这次升级的时候就强制客户端将代码中的ZK注册中心删除?之后直接从apollo配置中心删除zk注册中心
待解决问题
- Dubbo管控台默认只是展示一个注册中心的服务悉信息,用了多注册中心后,该如何方便的查看所有注册中心的服务注册情况?
- Dubbo管控台目前还没有etcd,需要改造