文章目录
本文主要参考自Dubbo官方文档、Dubbo项目源码以及网络文章和相关书籍,并附上自身的一些理解,如有遗漏或错误,还望海涵并指出。谢谢!
------本文基于Dubbo-2.6.1版本
一、Zookeeper在Dubbo中的作用
Dubbo源码(3)-基础抽象注册中心源码解析已经讲了注册中心抽象API的实现。
可以看到FailbackRegistry
基于AbstractRegistry
已经实现了失败重试的逻辑,最后和注册中心交互、写入、删除、查询的逻辑都使用模板方法模式将指定的方法下放给具体注册中心实现类来完成,这也是本章要说的重点。本章是对Zookeeper注册中心的解析,当然了,整个Dubbo注册中心体系下共有4个注册中心:
1.1、什么是Zookeeper
首先简单介绍一下,什么是Zookeeper?
我认为Zookeeper可以理解为一个分布式的数据管理系统,每个zk都采用类似于Linux的树状结构来管理文件,基于此可以构建出类似于服务注册、状态管理、集群管理等相关的应用来。
为什么选择zk作为注册中心呢,我认为有以下好处:
- 集群化配置方便,具备良好高可用性
- 根据ZAB算法做Leader选举,实时性高
- 整个zk集群能保证对外数据一致性
- 整个zk集群能保证数据的集群原子性
当然了,也可以使用Redis作为注册中心,并且Redis能够做到更快、读写效率更高;以CAP模型来看,Redis保证的是AP模型,也就是先保证可用性,但不保证数据的强一致性(只保证最终一致性),更加适合于作高速读写缓存;而zk保证的是CP模型,也就是先保证一致性,网络分区发生时有可能不对外提供服务。
具体选择Redis还是zk作为注册中心,还是要看具体情境,不过大部分情况下选择zk都是合适的。
1.2、Dubbo的Zookeeper注册中心
-
若Provider提供了
com.foo.BarService
这个接口的服务,那么在Provider启动时,会向 zk的/dubbo/com.foo.BarService/providers
目录下写入接口暴露出的URL地址10.20.153.10:20880
-
当Consumer启动时,会订阅zk的
/dubbo/com.foo.BarService/providers
目录下的Provider暴露的URL地址。并向/dubbo/com.foo.BarService/consumers
目录下写入自己的URL地址 -
当Monitor启动时,会订阅zk
/dubbo/com.foo.BarService
目录下的所有Provider和Consumer的URL地址。
以上就是以zk作为注册中心的使用,具体的使用细节以及配置参考官方文档:
https://dubbo.apache.org/zh-cn/docs/user/references/registry/zookeeper.html
二、Zookeeper注册中心源码解析
现在就是Zookeeper注册中心的源码解析环节了,先来看看大致的情况:
Zookeeper注册中心的实现位于dubbo-registry
模块下的dubbo-registry-zookeeper
子模块:
查看SPI文件,可知zookeeper
是RegistryFactory
的默认拓展实现:
zookeeper=com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory
代码量统计:
2.1、FailbackRegistry
这里就不贴具体代码了,主要是想提一下这里的模板方法模式:
public void register(URL url) {
// ...
try {
// !!doXxxx方法交由子类实现!!
doRegister(url);
} catch (Exception e) {
// ...
}
}
2.2、ZookeeperRegistryFactory
com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory
实现了AbstractRegistryFactory
抽象类,是ZookeeperRegistry
的工厂。
- ZookeeperTransporter
// 默认以curator作为zk客户端
@SPI("curator")
public interface ZookeeperTransporter {
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
ZookeeperClient connect(URL url);
}
- ZookeeperRegistryFactory
public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
// zk客户端
private ZookeeperTransporter zookeeperTransporter;
// 设置zk客户端
public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
this.zookeeperTransporter = zookeeperTransporter;
}
// 创建ZookeeperRegistry
public Registry createRegistry(URL url) {
return new ZookeeperRegistry(url, zookeeperTransporter);
}
}
2.3、ZookeeperRegistry
接下来就是本章的重点解析对象了,com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry
也就是真正的zk注册中心实现类,继承于FailbackRegistry
。从属性及构造方法、重点方法这两个维度来看源码:
2.3.1、属性及构造方法
// logger
private final static Logger logger = LoggerFactory.getLogger(ZookeeperRegistry.class);
// 默认zk端口号
private final static int DEFAULT_ZOOKEEPER_PORT = 2181;
// 默认zk根节点
private final static String DEFAULT_ROOT = "dubbo";
// zk根节点
private final String root;
// Service接口全名集合
private final Set<String> anyServices = new ConcurrentHashSet<String>();
//监听器集合
private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners = new ConcurrentHashMap<URL, ConcurrentMap<NotifyListener, ChildListener>>();
// zk客户端
private final ZookeeperClient zkClient;
/*
* URL:当前Registry的URL
* ZookeeperTransporter:指定zk客户端(默认为curator)
*/
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
// 获取到zk根结点
String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
if (!group.startsWith(Constants.PATH_SEPARATOR)) {
// 设置为自定义zk根结点
group = Constants.PATH_SEPARATOR + group;
}
// 设置为自定义zk根结点
this.root = group;
// 连接到指定的zk
zkClient = zookeeperTransporter.connect(url);
zkClient.addStateListener(new StateListener() {
public void stateChanged(int state) {
if (state == RECONNECTED) {
try {
// 断线重连zk时恢复自身数据
recover();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
});
}
2.3.2、重点方法
2.3.2.1、doRegister
- doRegister
/*
* 注册url到zk中
*/
protected void doRegister(URL url) {
try {
// 创建Root+Service+Type+Url的zk节点
// toUrlPath:获取到Root + Service + Type + URL
zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
- toUrlPath
/*
* 获取到Root + Service + Type + URL
*/
private String toUrlPath(URL url) {
return toCategoryPath(url) + Constants.PATH_SEPARATOR + URL.encode(url.toFullString());
}
- toCategoryPath
/*
* 获取到Root + Service + Type
*/
private String toCategoryPath(URL url) {
return toServicePath(url) + Constants.PATH_SEPARATOR + url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
}
- toServicePath
/*
* 获取到Root + Service
*/
private String toServicePath(URL url) {
String name = url.getServiceInterface();
if (Constants.ANY_VALUE.equals(name)) {
return toRootPath();
}
return toRootDir() + URL.encode(name);
}
- toRootDir
/*
* 获取到Root
*/
private String toRootDir() {
if (root.equals(Constants.PATH_SEPARATOR)) {
return root;
}
return root + Constants.PATH_SEPARATOR;
}
private String toRootPath() {
return root;
}
2.3.2.2、doUnregister
/*
* 取消注册,删除该Root+Service+Type+Url的zk节点
*/
protected void doUnregister(URL url) {
try {
zkClient.delete(toUrlPath(url));
} catch (Throwable e) {
throw new RpcException("Failed to unregister " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
2.3.2.3、doSubscribe
@Override
protected void doSubscribe(final URL url, final NotifyListener listener) {
try {
// 处理所有Service层的发起订阅,例如监控中心的订阅
if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
String root = toRootPath();
// 获得url对应的监听器集合
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) { // 不存在,进行创建
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);// 获得 ChildListener ChildListener zkListener = listeners.get(listener)if (zkListener == null) { // 不存在ChildListener 对象,进行创建ChildListener对象
listeners.putIfAbsent(listener, new ChildListener() public void childChanged(String parentPath, List<String> currentChilds) { for (String child : currentChilds) {
child = URL.decode(child);
if (!anyServices.contains(child)) {
anyServices.add(child);
subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
}
});
zkListener = listeners.get(listener);
}
// 创建 Service 节点。该节点为持久节点。
zkClient.create(root, false);
// 向 Zookeeper ,Service 节点,发起订阅
List<String> services = zkClient.addChildListener(root, zkListener);
// 首次全量数据获取完成时,循环 Service 接口全名数组,发起该 Service 层的订阅
if (services != null && !services.isEmpty()) {
for (String service : services) {
service = URL.decode(service);
anyServices.add(service);
subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
// 处理指定 Service 层的发起订阅,例如服务消费者的订阅
} else {
// 子节点数据数组
List<URL> urls = new ArrayList<URL>();
// 循环分类数组
for (String path : toCategoriesPath(url)) {
// 获得 url 对应的监听器集合
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) { // 不存在,进行创建
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
// 获得ChildListener对象
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) { // 不存在 ChildListener 对象,进行创建ChildListener对象
listeners.putIfAbsent(listener, new ChildListener() {
public void childChanged(String parentPath, List<String> currentChilds) {
// 出现变更,回调NotifyListener
ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
}
});
zkListener = listeners.get(listener);
}
// 创建Type节点。该节点为持久节点。
zkClient.create(path, false);
// 向Zookeeper ,PATH节点,发起订阅
List<String> children = zkClient.addChildListener(path, zkListener);
// 添加到urls 中
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
// 首次全量数据获取完成时,调用notify方法,回调 NotifyListener
notify(url, listener, urls);
}
} catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
2.3.2.4、doUnsubscribe
@Override
protected void doUnsubscribe(URL url, NotifyListener listener) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners != null) {
ChildListener zkListener = listeners.get(listener);
if (zkListener != null) {
// 通知zk ,移除订阅
zkClient.removeChildListener(toUrlPath(url), zkListener);
}
}
}