ServerCnxn存储
客户端并不把WherRegistration对象传递给服务端,那么服务端是怎么处理watcher注册的呢?
服务端在收到客户端的请求之后,在FinalRequestProcess.processRequest()方法中会判断当前请求是否需要watcher, 如果需要就会把当前的 ServerCnxn 对象 cnxn 传递进去:
case OpCode.getData:{
.......
byte[] b = zks.getZKDatabase.getData(getDataRequest.getPath(), stat, getDataReqest.getWatcher()? cnxn : null) ;
resp = new GetDataReqest(b,stat);
break ;
.....
}
ServerCnxn 是一个Zookeeper 客户端和服务器端之间的链接接口,代表了一个客户端和服务端的链接。默认实现是 NIOServerCnxn ,还有一个 NettyCnxn 。这两个实现了 Watcher 的 process接口,所以ServerCnxn 实际上也是一个Watcher对象。
数据节点路径和ServerCnxn 最终会被存储在WatcherManager中的 watchTable 和 watch2Path 中,这两个边路都是 类型的数据结构,从两个维度进行存储Watcher.
watchTable : Map<String,HashSet<Watche>> , 从数据节点路径的粒度托管 wacher
watch2Path : Map<Watche,HashSet<String>> , 从Watche的粒度来控制事件触发需要触发的数据节点。
在服务端,DataTree会托管两个WatchManager ,分别是 dataWatches 和 childrenWatches .
watcher触发
对于标记了watch 的请求,zookeeper 是把 ServerCnxn 作为 Watcher 存 储到是 WatcherManager中的,对于注册了 NodeDataChanaged 的监听事件,触发请求的逻辑是:
public Stat setData(String path, .....){
Stat s = new Stat();
DataNode n = nodes.get(path) ;
..........
dataWatches.triggerWatch(path, EventType.NodeDataChanged) ;
return s ;
}
dataWatches.triggerWatch 会触发相关的事件:
public Set<Watcher> triggerWatch(String path, EventType type){
return triggerWatch(path,type,null);
}
public Set<Watcher> triggerWatch(String path, EventType type, Set<Watcher> supress){
WatchedEvent we = new WatchedEvent(type, KeepState.SyncConnected, path);
HashSet<Watcher> watchers ;
synchronized(this){
watches = watchTable.remove(path) ;//删除该路径注册的所有监听
//再删除 watch2Path 数据结构中的path
for(Watcher w : watches){
Set<String> paths = watch2Path.get(w) ;if(paths != null){paths.remove(path); //删除路径}
}
//通过ServerCnxn通知客户端
for( Watcher w : watches){
if(supres !=null && supres.concatins(w)){contine;}w.process(we) ; //根据上面的了解,此时 w 实际上是一个ServerCnxn 对象。而ServerCnxn 代表了一个客户端和服务端的链接,所以Zookeeper实际上是没有直接处理Watcher的逻辑的,而是借助当前客户端链接的ServerCnxn 对象来实现对客户端的WatchedEvent 的传递,让客户端去处理的。
}
return watches ;
}
无论dataWathches 还是 childrenWatches 管理器,执行逻辑的是一样的,如下:
步骤四中触发Watcher的逻辑如下:
因为对于需要注册Watcher的请求,ZK是把当前请求的ServerCnxn作为一个Watcher进行存储的,所以此处process方法实质上就是ServerCnxn的方法
public class NIOServerCnxn exnteds ServerCnxn{
Synchronized public void process(WatchedEvent event){
ReplyHeader h = new ReplyHeader(-1,-1L, 0) ;//标记当前是一个通知
.......
WatcherEvent we = event.getWrapper(); // 包装成可序列后进行网络传输的对象
sendResponse(h , we, "notification") ; // 向客户端发送通知
}