相关类
Watcher 接口
任何一个事件处理类都必须实现Watcher
接口,它有一个内部接口 Event
,以及 process
方法。前者定义了ZK的状态和事件类型的枚举,后者则定义了事件的回调方法。
@InterfaceAudience.Public
public interface Watcher {
@InterfaceAudience.Public
public interface Event {
@InterfaceAudience.Public
public enum KeeperState {
/*...*/}
@InterfaceAudience.Public
public enum EventType {
/*...*/}
}
abstract public void process(WatchedEvent event);
}
回调方法 process
只有一个参数 WatchedEvent
,这个类也很简单,只有三个参数,分别是通知状态、事件类型,以及节点路径。
public class WatchedEvent {
final private KeeperState keeperState;
final private EventType eventType;
private String path;
/*...*/
}
和 WatchedEvent
紧密相关的还有 WatcherEvent
,两者都是对ZK服务端事件的封装,不过后者实现了序列化接口,可用于网络传输。
ZK服务端会将 WatchedEvent
包装成 WatcherEvent
进行传输,客户端则需逆向处理,解包装成WatchedEvent
来处理事件。
可以看到,WatchedEvent
和 WatcherEvent
都只有简单的事件本身的信息,而不包含具体的内容,因此需要客户端主动去获取感兴趣的最新数据。
工作机制
客户端注册
客户端可以通过 getData()
、getChildren()
和exist()
三个接口来向服务端注册 Watcher。注册的原理都是相同的,这里仅以 getData()
为例进行分析。
getData
有两个重载方法,区别在于第二个参数。
// 使用自定义的 watcher
public byte[] getData(final String path, Watcher watcher, Stat stat)
// 是否使用默认的 watcher
public byte[] getData(String path, boolean watch, Stat stat)
默认的 watcher 在实例化 ZooKeeper
的时候指定:
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)
ZooKeeper
实例维护了一个 ZKWatchManager
类的实例,该类实现了ClientWatchManager
接口,是客户端的 watcher 管理器。
默认 watcher 就保存在 ZKWatchManager
的 defaultWatcher
中。
private final ZKWatchManager watchManager = new ZKWatchManager();
private static class ZKWatchManager implements ClientWatchManager {
private final Map<String, Set<Watcher>> dataWatches =
new HashMap<String, Set<Watcher>>();
private final Map<String, Set<Watcher>> existWatches =
new HashMap<String, Set<Watcher>>();
private final Map<String, Set<Watcher>> childWatches =
new HashMap<String, Set<Watcher>>();
private volatile Watcher defaultWatcher;
/* ... */
}
回到getData
,传递 watcher 参数后,首先会被封装成 WatchRegistration
对象,并设置 request 对象为“使用watcher监听”:
public byte[] getData(final String path, Watcher watcher, Stat stat)
throws KeeperException, InterruptedException {
/* ... */
WatchRegistration wcb = null;
if (watcher != null) {
wcb = new DataWatchRegistration(watcher, clientPath);
}
/* ... */
request.setWatch(watcher != null);
ReplyHeader r = cnxn.submitRequest(h, request, response, wcb);
/* ... */
}
然后在客户端连接对象cnxn(ClientCnxn
)的 submitR