Dubbo注册中心 Zookeeper原理与实现
点关注不迷路,欢迎再访!
精简博客内容,尽量已行业术语来分享。
努力做到对每一位认可自己的读者负责。
帮助别人的同时更是丰富自己的良机。
一.Zookeeper原理介绍
Zookeeper 是树形结构的注册中心,Dubbo使用zookeeper作为注册中心时,只会创建持久节点和临时节点两种,对创建的顺序并没有要求。结构如下:
+/dubbo
±- service
±- providers
± - consumers
± - routers
± - configurators
二.Zookeeper 的实现
2.1 发布的实现
服务提供者:注册是为了让消费者感知服务的存在,从而发起远程调用;也让服务治理中心感知有新的服务提供者上线;
消费者 :发布是为了让服务治理中心可以发现自己。
zkClient创建目录源码
zkClient.create(toUrlPath(url),url.getParameter(Constants.DYNAMIC_KEY,true));
2.2 订阅的实现
订阅常用方式:
pull : 客户端定时轮询注册中心拉取配置;
push : 注册中心主动推送数据给客户端。
目前dubbo采用pull方式,后续接收事件重新拉取数据。采用“事件通知” + “客户端拉取”的方式,客户端第一次连接上注册中心时,会获取对应目录下全量的数据。并再订阅的节点上注册一个watcher,客户端与注册中心之间保持TCP长连接,后续每个节点有任何数据变化,注册中心会根据watcher的回调主动通知客户端,客户端接到通知后会把对应节点下的权力数据都拉取过来。
消费者的订阅逻辑:
根据URL的类别得到一组需要订阅的路径。如果类别是*,则会订阅所有类型路径,否则只订阅providers路径,源码:
List<URL> urls = new ArrayList<URL>();
//根据URL类别,获取一组要订阅的路径
for(String path : toCategoriesPath(url)){
ConcurrentMap<NotifyListener , ChildListener> listeners = zkListeners.get(url);
//如果listeners缓存为空则创建缓存
if(listeners == null){
zkListeners.putIfAbsent(url , new ConcurrentHashMap<NotifyListener , ChildListener>);
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners .get(listener);
//如果zkListener缓存为空则创建缓存
if(zkListener == null){
listeners.putIfAbsent(listener , new ChildListener (){
@Override
public void childChanged(String parentPath , List<String> currentChilds){
ZookeeperRegistry.this.notify(url , listener , toUrlsWithEmpty(url , parentPath , currentChilds));
}
});
zkListener = listeners.get(listener);
}
zkClient.create(path , false);
//订阅,返回该节点下的子路径并缓存
List<String> children = zkClient.addChildListener(path , zkListener);
if(children != null){
urls.addAll(toUrlsWithEmpty(url ,path ,children));
}
}
//回调NotifyListener, 更新本地缓存信息
notify(url , listener ,urls);
注意,根据URL中category类别,然后拉取直接子节点的数据进行通知(notify)。
providers 类别:订阅方会更新本地Directory管理的Invoker服务列表;
routers类别:订阅方会更新本地路由规则列表;
configuators类别:订阅方会更新或覆盖本地动态参数列表。