如果有人问你 Dubbo 中注册中心工作原理,就把这篇文章给他

注册中心作用
开篇首先想思考一个问题,没有注册中心 Dubbo 还能玩下去吗?
当然可以,只要知道服务提供者地址相关信息,消费者配置之后就可以调用。如果只有几个服务,这么玩当然没问题。但是生产服务动辄成千上百,如果每个服务都需要手写配置信息,想象一下是多么麻烦。
好吧,如果上面的问题都不是事的话,试想一下如果一个服务提供者在运行过程中宕机,消费者怎么办?消费者不知情,所以它还会不断把请求发往服务提供者,然后不断失败。这个时候唯一的办法就是修改服务地址信息,然后重启服务。
可以看到如果没有注册中心,分布式环境中服务查找发现将会非常麻烦,一切需要手工配置,无法完成自动化。所以这里就需要一个第三者,协调服务提供者与消费者之间关系,这就是注册中心。
注册中心主要作用如下:
- 动态加入,服务提供者通过注册中心动态的把自己暴露给消费者,无需消费者逐个更新配置文件。
- 动态发现服务,消费者可以动态发现新的服务,无需重启生效。
- 统一配置,避免本地配置导致每个服务配置不一致。
- 动态调整,注册中心支持参数动态调整,新参数自动更新到所有相关的服务节点。
- 统一管理,依靠注册中心数据,可以统一管理配置服务节点。
注册中心工作流程
注册中心工作流程总体比较简单,流程图大致如下:

主要工作流程可以分为如下几步:
- 服务提供者启动之后,会将服务注册到注册中心。
- 消费者启动之后主动订阅注册中心上提供者服务,从而获取到当前所有可用服务,同时留下一个回调函数。
- 若服务提供者新增或下线,注册中心将通过第二步的注册的回调函数通知消费者。
- dubbo-admin(服务治理中心)将会会订阅服务提供者以及消费者,从而可以在控制台管理所有服务提供者以及消费者。
Dubbo 之前版本主要可以使用 ZooKeeper,Redis 作为注册中心 ,而随着 Dubbo 版本不断更新,目前还支持 nacos,consul,etcd 等做为注册中心。
Dubbo 注册中心核心源码
ps: 以下源码基于 dubbo 2.7.3 版本
注册中心实现使用模板模式,源码位于 dubbo-registry 模块,类关系如下图:

最上层的 RegistryService 接口定义了核心方法,分别为注册,取消注册,订阅,取消订阅以及查询。

中间层抽象类主要实现通用逻辑,如:AbstractRegistry 实现缓存机制,FailbackRegistry 实现失败重试功能。
底层 ZookeeperRegistry等为具体实现类,实现与 ZooKeeper 等注册中心交互的逻辑。
接下去我们具体分析 AbstractRegistry 与 FailbackRegistry 逻辑。
AbstractRegistry 缓存实现的原理
如果每次服务调用都需要调用注册中心实时查询可用服务列表,不但会让注册中心承受巨大的流量压力,还会产生额外的网络请求,导致系统性能下降。
其次注册中心需要非强依赖,其宕机不能影响正常的服务调用。
基于以上几点,注册中心模块在 AbstractRegistry 类中实现通用的缓存机制。这里的缓存可以分为两类,内存服务缓存以及磁盘文件缓存。
内存服务缓存
内存服务缓存很好理解也最容易实现,AbstractRegistry使用一个 ConcurrentMap保存相关信息。
private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<>();
这个集合中 key 为消费者的 URL,而 value 为一个 Map 集合。这个内层 Map 集合使用服务目录作为 key,分别为 providers,routers,configurators,consumers 四类,value 则是对应服务列表集合。
磁盘文件缓存
由于服务重启就会导致内存缓存消失,所以额外增加磁盘文件缓存。
文件缓存默认位置位于 ${user.home}/.dubbo/文件夹,文件名为dubbo-registry-${application.name}-${register_address}.cache。可以设置 dubbo.registry.file 配置信息从而修改默认配置,实现源码如下:
String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getParameter(Constants.APPLICATION_KEY) + "-" + url.getAddress() + ".cache");
ps: ${application.name} 取自
dubbo.application.name信息,${register_address} 取值注册中心地址信息。缓存文件完整名称为:C:\Users\xxx/.dubbo/dubbo-registry-dubbo-auto-configure-consumer-sample-127.0.0.1:2181.cache
缓存文件内容使用 properties 配置文件格式,即 key=value 格式。key为服务接口名称,value 为服务列表,由于服务可能存在多个,将会使用空格分隔。
缓存文件的加载
dubbo 程序初始化的时候,AbstractRegistry 构造函数将会从本地磁盘文件中将数据读取到 Properties 对象实例中,后续都将会先写入 Properties,最后再将里面信息再写入文件。
缓存初始化的源码为下图。

缓存文件的保存与更新
缓存文件将会通过 AbstractRegistry#notify 方法保存或更新。客户端第一次订阅服务获取的全量数据,或者后续回调中获取到新数据,都将会调用 AbstractRegistry#notify 方法,用来更新内存缓存以及文件缓存。
notify 方法源码如下图:

在保存文件缓存方法中,首先把根据 URL 取出的数据,拼接成字符串,然后写入上面提到过的 properties 对象中,最后输出到文件中。
这里可以选择两种保存方式,同步或异步。由于 notify 可能被多次调用,为了提高系统能,系统默认使用异步方式保存。
saveProperties 方法源码如下:

doSaveProperties 方法最终将会将信息写入缓存。考虑到保存方法可能会被多个线程同时调用,这里使用 CAS 方法,首先比较版本大小,若小于,代表有新线程正在写入信息,本次更新直接丢弃。
其次考虑到多个 dubbo 应用可能共用一份缓存文件,所以这里使用文件排他锁当做分布式锁,防止多个应用并发操作同一份文件。
一旦文件写入异常或者获取锁失败,保存操作将会不断重试,直到超过最大次数。
ps: dubbo 2.7.2 之前重试没有设置最大次数,如果文件没有权限保存,保存将会一直失败,异步线程将会陷入死循环。
doSaveProperties 方法源码如下:

FailbackRegistry 重试机制
FailbackRegistry 继承 AbstractRegistry,实现了 register,subscribe等通用法,并增加 doRegister,doSubscribe 等模板方法,交由子类实现。
如果 doRegister 等模板方法发生异常,会将失败任务放入集合,然后定时再次调用模板方法。
FailbackRegistry 失败重试集合分别为:

以 subscribe 方法为例,这里将会调用这些 doSubscribe 的模板方法。如果发生异常将会读取缓存文件中内容,然后加载服务。最后新建异步定时任务加入重试集合中,然后由定时器去重试这些任务。
FailbackRegistry#subscribe 方法源码:

在 addFailedSubscribed 中将会新建定时任务,然后交由定时器执行。定时任务默认最大重试次数为 3 次,调用时间间隔默认为 5 s。
addFailedSubscribed 源码如下:

其他失败重试任务都比较类似,全都继承自 AbstractRetryTask 父类,类关系如下图。

总结
本文主要讲述注册中心作用,工作流程,通用缓存机制以及失败重试机制。从中可以学到模板模式,以及多线程并发技巧。
这里没有涉及到具体注册中心实现,由于目前最主要使用 ZooKeeper 作为注册中心,所以下篇将会聊聊 ZooKeeper 注册中心原理,敬请期待。
帮助书籍
『深入理解 Apache Dubbo 与实战』

如果有人问你 Dubbo 中注册中心工作原理,就把这篇文章给他的更多相关文章
- Dubbo多注册中心和Zookeeper服务的迁移
一.Dubbo多注册中心 1. 应用场景 例如阿里有些服务来不及在青岛部署,只在杭州部署,而青岛的其它应用需要引用此服务,就可以将服务同时注册到两个注册中心. consumer.xml <?xm ...
- Dubbo配置注册中心设置application的name使用驼峰命名法存在的隐藏项目启动异常问题
原创/朱季谦 首先,先提一个建议,在SpringBoot+Dubbo项目中,Dubbo配置注册中心设置的application命名name的值,最好使用xxx-xxx-xxx这样格式的,避免随便使用驼 ...
- 转:ListView中getView的工作原理
ListView中getView的工作原理: [1]ListView asks adapter “give me a view” (getView) for each item of the list ...
- 本版本延续MVC中的统一验证机制~续的这篇文章,本篇主要是对验证基类的扩展和改善(转)
本版本延续MVC中的统一验证机制~续的这篇文章,本篇主要是对验证基类的扩展和改善 namespace Web.Mvc.Extensions { #region 验证基类 /// <summary ...
- 【DUBBO】zookeeper在dubbo中作为注册中心的原理结构
[一]原理图 [二]原理图解释 流程:1.服务提供者启动时向/dubbo/com.foo.BarService/providers目录下写入URL2.服务消费者启动时订阅/dubbo/com.foo. ...
- Javaconfig形式配置Dubbo多注册中心
多注册中心,一般用不到,但是某些情况下的确能解决不少问题,可以将某些dubbo服务注册到2套dubbo系统中,实现服务在2套系统间的共用. 网上的配置说明很多,但包括dubbo官方说明文档都是以xml ...
- Dubbo及注册中心原理
本文首发于微信公众号[猿灯塔],转载引用请说明出处 今天是猿灯塔“365天原创计划”第4天. 今天呢!灯塔君跟大家讲: 一.Dubbo意义 网站应用的架构变化经历了一个从所有服务分布在一台服务器上(A ...
- Dubbo多注册中心
一.创建提供者08-provider-registers (1) 创建工程 直接复制05-provider-group工程,并命名为08-provider-registers (2) 修改配置文件 二 ...
- Dubbo Multicast 注册中心即相关代码实现
Dubbo 的 Multicast注册中心有下面特点: 不需要启动任何中心节点,只要广播地址一样,就可以互相发现 组播受网络结构限制,只适合小规模应用或开发阶段使用. 组播地址段: 224.0.0.0 ...
随机推荐
- Balking设计模式
import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayLi ...
- TCP、UDP和HTTP简述整理
http:是用于www浏览的一个协议.tcp:是机器之间建立连接用的到的一个协议. 1.TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层.在网络层有IP协议.ICMP协议.ARP协议.R ...
- UVA11988 【Broken Keyboard (a.k.a. Beiju Text)】:题解
题目链接:https://www.luogu.org/problemnew/show/UVA11988 这题虽说是和链表有关的模拟,但其实并不是很需要啊,但蒟蒻用了(说的好听是练手,说的难听是太弱), ...
- RabbitMQ 从入门到精通(二)
目录 1. 消息如何保障百分之百的投递成功? 1.1 方案一:消息落库,对消息状态进行打标 1.2 方案二:消息的延迟投递,做二次确认,回调检查 2. 幂等性 2.1 幂等性是什么? 2.2 消息端幂 ...
- Codeforces1144D(D题)Equalize Them All
D. Equalize Them All You are given an array aa consisting of nn integers. You can perform the follow ...
- 微信小程序设计总结
微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验. 小程序提供了一个简单.高效的应用开发框架和丰富的组件及API,帮助开发者在微信中开发具有原生 A ...
- static import和import的区别
import static静态导入是JDK1.5中的新特性.一般我们导入一个类都用 import com.....ClassName;而静态导入是这样:import static com.....Cl ...
- JS中构造函数和普通函数有什么区别
JS中构造函数有普通函数有什么区别? 1.一般规则 构造函数都应该以 一个大写字母开头,eg: function Person(){...} 而非构造函数则应该以一个小写字母开头,eg: functi ...
- itextpdf 解析带中文的html问题
官网连接 官网上有很多DEMO,下面就说几个我碰到的问题! Question: 1. 中文不显示 或者是乱码(本打算用Apache pdfbox来实现业务,但是折腾了一个上午也没解决中午乱码问题,就找 ...
- 【pip】brew install pip 问题
Mac 下用 brew install pip 命令安装 pip 时报错: Error: No available formula with the name "pip" Home ...