Zookeeper中的watcher机制;watcher事件和zookeeper状态;watcher的一次性
watcher架构
Watcher实现由三个部分组成:
- Zookeeper服务端;
- Zookeeper客户端;
- 客户端的ZKWatchManager对象;
客户端首先将Watcher注册到服务端,同时将Watcher对象保存到客户端的Watch管理器中。当ZooKeeper服务端监听的数据状态发生变化时,服务端会主动通知客户端,接着客户端的Watch管理器会触发相关Watcher来回调相应处理逻辑,从而完成整体的数据发布/订阅流程。
watcher事件和zookeeper状态
Watcher是一个接口,任何实现了Watcher接口的类就是一个新的Watcher。Watcher内部包含了两个枚举类:KeeperState、EventType.
EventType事件类型:(znode节点相关的)
EventType:NodeCreated //节点创建
EventType:NodeDataChanged //节点的数据变更
EventType:NodeChildrentChanged //子节点下的数据变更
EventType:NodeDeleted //节点删除
KeeperState状态类型:(是跟客户端实例相关的)
KeeperState:Disconneced //连接失败
KeeperState:SyncConnected //连接成功
KeeperState:AuthFailed //认证失败
KeeperState:Expired //会话过期
注意:当KeeperState发生变化时,触发的事件EventType为None。
客户端如何注册Watch?
(1)创建zk客户端实例的时候注册watcher(构造方法中注册watcher);
(2)调用 getData()/getChildren()/exist()三个 API,传入 Watcher 对象;
Watch特性
一次性:Watcher是一次性的,事件一旦被触发就会移除,再次使用时需要重新注册
客户端顺序回调:Watcher回调是顺序串行化执行的,只有回调后客户端才能看到最新的数据状态。一个Watcher回调逻辑不应该太多,以免影响别的watcher执行
轻量级:WatchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径,并不会告诉数据节点变化前后的具体内容;
时效性:Watcher只有在当前session彻底失效时才会无效,若在session有效期内快速重连成功,则watcher依然存在,仍可接收到通知;
测试
package com.weng.zk; import org.apache.zookeeper.*; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.List; public class zkClient { private String connectString="hadoop102:2181,hadoop103:2181,hadoop104:2181"; private int sessionTimeout=100000; private ZooKeeper zkClient = null; @Before public void init() throws IOException { //使用zk客户端实例注册一个默认Watcher zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() { public void process(WatchedEvent watchedEvent) { if ( watchedEvent.getType() == null || "".equals( watchedEvent.getType())) { return; } System.out.println("默认watcher"); System.out.println("连接状态:"+watchedEvent.getState()); System.out.println("已经触发了" + watchedEvent.getType() + "事件!"); } }); } @Test public void getChildren() throws KeeperException, InterruptedException { //使用getChildren()注册一个普通Watcher List<String> children = zkClient.getChildren("/", new Watcher() { public void process(WatchedEvent watchedEvent) { System.out.println("普通watcher"); System.out.println("连接状态:"+watchedEvent.getState()); System.out.println("已经触发了" + watchedEvent.getType() + "事件!"); } }); Thread.sleep(Long.MAX_VALUE); } }
测试结果1
测试代码2
package com.weng.zk; import org.apache.zookeeper.*; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.List; public class zkClient { private String connectString="hadoop102:2181,hadoop103:2181,hadoop104:2181"; private int sessionTimeout=100000; private ZooKeeper zkClient = null; @Before public void init() throws IOException { //使用zk客户端实例注册一个默认Watcher zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() { public void process(WatchedEvent watchedEvent) { if ( watchedEvent.getType() == null || "".equals( watchedEvent.getType())) { return; } System.out.println("默认watcher"); System.out.println("连接状态:"+watchedEvent.getState()); System.out.println("已经触发了" + watchedEvent.getType() + "事件!"); } }); } @Test public void getChildren() throws KeeperException, InterruptedException { //使用getChildren()时传入默认Watcher List<String> children = zkClient.getChildren("/",true); Thread.sleep(Long.MAX_VALUE); } }
测试结果2
(以上测试均保证了Watcher的时效性)
总结
普通Watcher触发节点事件后立即消亡,节点事件和服务器状态改变都不再触发。
默认Watcher伴随整个会话,触发节点事件后不会消亡,但对节点事件不再触发,对服务器状态改变仍会触发。