Dubbo源码解析——注册中心的容错策略
简介
其实之前一直是我遗漏了这个部分的逻辑,没有细看。后来偶然间在某个PR中看到了注册中心中还进行了文件存储相关的操作,就好奇的看了一下这个部分的逻辑。
这里主要的作用是增加注册中心的健壮性,我们需要考虑在某些场景下,注册中心和应用之间的网络抖动对全局的影响:
- 对于
Provider
来说,注册失败就直接启动失败,默认情况下,Zookeeper
和Provider
之间的网络抖动我们可以不做处理,默认Dubbo创建的是一个临时节点,会自动通过心跳来探活(很多人都提过ISSUE问过这个问题,知识点)。Provider
目前不订阅注册中心上的信息,所以不需要考虑订阅时失败。 - 对于
Consumer
来说,默认情况下,注册自己的时候失败就直接返回(比如Consumer
和注册中心网络不通)。考虑一下一个Consumer
订阅信息失败的时候我们如何处理,直接失败?假如网络抖动导致我们某一次启动Consumer
的时候订阅失败,除了定时重试,我们是否还可以有更优秀的做法来保证程序的健壮。
这就是关于注册中心的容错策略。具体的做法就是,每次注册中心上,Provider
变更的消息通知到本地的时候(目前Dubbo默认情况下还是全量通知),我们把最新的消息保留下来,当我们在重启Consumer
的时候,或者Consumer
和注册中心之间的网络抖动的时候,我们至少可以把最近的Provider
信息告知Consumer
,让Consumer
先尝试连接这些Provider
,而不是直接让Consumer
重启失败。
需要注意的是,容错的机制主要发生在订阅阶段,所以我们下面主要关注订阅失败相关的处理。
通知流程
先看我们的启动流程,注册中心启动的时候,会load
我们已有的cache
文件(cache
文件就是我们说的最近一次的Provider
的相关数据,比如URL
),具体的代码在AbstractRegistry
的构造函数里:
1 | setUrl(url); |
根据我们之前分析的流程,我们需要关注每次Provider
变更通知到本地的时候,我们是如何处理的,AbstractRegistry#notify(org.apache.dubbo.common.URL, org.apache.dubbo.registry.NotifyListener, java.util.List<org.apache.dubbo.common.URL>)
:
1 | // 这里的key是category,值是对应的URL |
这里我们可以看到,最新的Provider
端URL
到达Consumer
的时候,我们会把它存到notified
这个全局变量里,然后通知listener
。
看下这个saveProperties
方法:
1 | StringBuilder buf = new StringBuilder(); |
注意这里我们只保存最新的一份数据,所以用一个version
来控制。看看这个doSaveProperties
方法:
1 | if (version < lastCacheChanged.get()) { |
总结一下整个流程就是:
- 最新的
URL
(全量的,之前已经说过)到达。 - 数据保存到全局变量中(
notified
)。 - 异步的保存到文件中,即使注册中心重启也不会丢失。
订阅过程的容错
看下这个方法FailbackRegistry#subscribe
:
1 | // 这里是订阅过程抛出异常时的处理 |
总结一下订阅的流程也很简单:
- 如果出错了,获取最新的
Provider
端URL副本。 - 通知给监听器。(只通知需要订阅这个监听器,并非所有的)
- 添加一个任务定时重新订阅,重试成功时,这时可能
Consumer
还维护的是Provider
的URL
的副本,可能是过期的,当重试任务成功后,会把最新的数据更新过来。
至此我们就完成了注册中心的容错流程分析。这里并不复杂,但是很容易遗漏,网上关于这个地方的分析不是很多,这里拿出来跟大家分享一下。