背景
前面分析了使用redis作为注册中心的代码 dubbo之redis注册中心
我们来看一下官方最推荐生产环境使用的zookeeper~
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
分析
作为注册中心我们还是要能够快速无误的获取到服务的上下线信息 对于zookeeper不要太简单
zookeeper提供了watch
Znode 发生变化(Znode 本身的增加,删除,修改,以及子 Znode 的变化)可以通过 Watch 机制通知到客户端。那么要实现 Watch,就必须实现 org.apache.zookeeper.Watcher 接口,并且将实现类的对象传入到可以 Watch 的方法中。Zookeeper 中所有读操作(getData(),getChildren(),exists())都可以设置 Watch 选项。Watch 事件具有 one-time trigger(一次性触发)的特性,如果 Watch 监视的 Znode 有变化,那么就会通知设置该 Watch 的客户端。
这个就是相当方便了~作为服务消费者我们需要做的就是监听到服务提供者的节点 对应的change事件收到即可!
protected void doSubscribe(final URL url, final NotifyListener listener) {
try {
if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
****
} else {
List<URL> urls = new ArrayList<URL>();
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
listeners.putIfAbsent(listener, new ChildListener() {
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));
}
}
notify(url, listener, urls);
}
} catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
我们只需要在对应的节点增加监听即可~
dubbo通过自定义的childListener适配到多个不同的zk客户端~
很明显这样就完成关于服务上下线的监听
我们需要注意的是zkClient创建了节点 其中节点调用了如下方法
zkClient.create(path, false);
这边会创建多个节点 【主要指需要订阅通知的】包括providers 这边的false表示为持久节点
我们来看一下如何注册
protected void doRegister(URL url) {
try {
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);
}
}
这边可以看到会根据是否动态创建对应的节点 一般情况下该节点为临时节点
Ephemeral节点在客户端session结束或超时后自动删除
因此当服务下线或者未正常下线【比如超时 网络分区等】
这样该节点将会被移除掉
因此进行监听节点的将会受到nodechanged事件~从而获得最新的服务列表等