前面分析了使用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事件~从而获得最新的服务列表等