Zookeeper的Watch机制提供了服务通知功能,包含客户端的注册和触发、服务端的注册和触发
image.png
Zookeeper客户端:
通过ZKWatchManager管理
结构都是存储Map<String, Set<Watcher>>
例如:{"/abc",Set<Watcher>} 表示/abc节点下绑定了Watcher有哪些
1、临时的一次性的,用完即删除有dataWatches、existWatches、childWatches
2、永久性的有persistentWatches、persistentRecursiveWatches(表示递归的永久性Watcher)
Zookeeper服务端:
有dataWatches和childWatches
每个属性下面都有watchTable、watch2Paths。
服务端的Watcher表示当前客户端的实现类NioServerCnxn
watchTable:Map<String, Set<Watcher>>
例如:{"/abc",Set<Watcher>} 表示/abc节点下有哪些客户端含有Watcher
watch2Paths: Map<Watcher, Set<String>>
例如:{Watcher,Set<String>} 表示当前客户端下有那些路径下有Watcher
客户端注册
当调用自定义getData方法时
image.png
1、getData
这里并没有注册
org.apache.zookeeper.ZooKeeper#getData
image.png
①、把watcher和clientPath封装成DataWatchRegistration对象
org.apache.zookeeper.ZooKeeper.DataWatchRegistration#DataWatchRegistration
image.png
org.apache.zookeeper.ZooKeeper.WatchRegistration#WatchRegistration
image.png
②、提交请求submitRequest
把封装的对象使用cnxn.submitRequest方法提交请求,最终把DataWatchRegistration wcb赋值给数据包Packet的watchRegistration属性
image.png
2、通过SendThread线程的readResponse读取响应数据
①、readResponse
org.apache.zookeeper.ClientCnxn.SendThread#readResponse
image.png
②、finishPacket
org.apache.zookeeper.ClientCnxn#finishPacket
image.png
③、register
通过数据包的WatchRegistration属性值注册watch
org.apache.zookeeper.ZooKeeper.WatchRegistration#register
image.png
org.apache.zookeeper.ZooKeeper.DataWatchRegistration#getWatches
获取的是ZKWatchManager#dataWatches属性
image.png
最终把当前watch注册给了ZKWatchManager的dataWatches属性值。
服务端注册
服务端的注册是在请求处理链的最后一个请求处理类FinalRequestProcessor的processRequest方法来调用的
客户端是以zooKeeper.getData方法来测试的,所以直接到getData
org.apache.zookeeper.server.FinalRequestProcessor#processRequest
image.png
org.apache.zookeeper.server.FinalRequestProcessor#handleGetDataRequest
image.png
org.apache.zookeeper.server.ZKDatabase#getData
image.png
org.apache.zookeeper.server.DataTree#getData
image.png
org.apache.zookeeper.server.watch.WatchManager#addWatch
image.png
org.apache.zookeeper.server.watch.WatchManager#addWatch
①、watchTable
Map<String, Set<Watcher>> 表示一个路径有多少个客户端Watcher(NioServerCnxn对象)
image.png
②、watch2Paths
Map<Watcher, Set<String>> 表示一个客户端Watcher(NioServerCnxn对象)有多少个路径
image.png
dataWatches和childWatches都是添加watchTable、watch2Paths的属性值
dataWatches和childWatches是在DataTree创建对象时创建的,都属于IWatchManager对象
image.png
客户端触发
是在SendThread线程读取响应readResponse时
1、添加到waitingEvents队列
NOTIFICATION_XID = -1 是触发WATCHER_EVENT
①、readResponse
org.apache.zookeeper.ClientCnxn.SendThread#readResponse
image.png
image.png
②、queueEvent
image.png
org.apache.zookeeper.ClientCnxn.EventThread#queueEvent
image.png
把当前响应事件添加到waitingEvents队列中
2、一次性Watch与持久Watch的区别
在添加waitingEvents队列之前,调用materialize方法根据路径查找客户端的Watch
org.apache.zookeeper.ZKWatchManager#materialize
image.png
image.png
dataWatches把当前节点的watch给移除了,这就是为什么zooKeeper.getData方法的watch只能调用一次
而持久化节点并没有移除
image.png
3、调用watch的process方法
便可以调用到客户端自定义的监听watch的process方法
org.apache.zookeeper.ClientCnxn.EventThread#run
image.png
org.apache.zookeeper.ClientCnxn.EventThread#processEvent
image.png
服务端触发
服务端的触发也是在请求处理链的最后一个请求处理类FinalRequestProcessor的processRequest方法来调用的
1、processRequest
org.apache.zookeeper.server.FinalRequestProcessor#processRequest
image.png
2、applyRequest
org.apache.zookeeper.server.FinalRequestProcessor#applyRequest
image.png
3、processTxn
org.apache.zookeeper.server.ZooKeeperServer#processTxn(org.apache.zookeeper.server.Request)
image.png
4、processTxnInDB
org.apache.zookeeper.server.ZooKeeperServer#processTxnInDB
image.png
5、processTxn
org.apache.zookeeper.server.ZKDatabase#processTxn
image.png
org.apache.zookeeper.server.DataTree#processTxn
image.png
org.apache.zookeeper.server.DataTree#processTxn
image.png
org.apache.zookeeper.server.DataTree#processTxn
image.png
6、setData
这里还是以/abc节点为例,开启一个客户端修改/abc的值。调用命令set /abc xxx。直接到setData
这里可以看到修改前、修改后的数据、路径、zxid等信息
org.apache.zookeeper.server.DataTree#setData
image.png
触发NodeDataChanged事件类型
image.png
7、triggerWatch
org.apache.zookeeper.server.watch.WatchManager#triggerWatch
image.png
org.apache.zookeeper.server.watch.WatchManager#triggerWatch
可以看到当前路径(/abc)下的上下文客户端对象
image.png
org.apache.zookeeper.server.watch.WatchManager#triggerWatch
这里调用了递归的文件,最终的效果就是查找当前路径下的客户端对象
image.png
image.png
遍历Watch对象的process方法
image.png
8、process
org.apache.zookeeper.server.NIOServerCnxn#process
image.png
响应头设置ClientCnxn.NOTIFICATION_XID,这里与客户端读取响应头相对应
9、sendResponse
发送响应数据到客户端
org.apache.zookeeper.server.NIOServerCnxn#sendResponse
image.png
EventType事件类型
None、NodeCreated(节点创建)、NodeDeleted(节点删除)、NodeDataChanged(节点数据修改)、NodeChildrenChanged(孩子节点改变)、DataWatchRemoved(监听器Watch移除)、ChildWatchRemoved(孩子监听器Watch移除)、PersistentWatchRemoved(持久化的监听器Watch移除)
image.png
总结:
根据客户端使用不同的API触发不同的监听器Watch。
客户端的注册是在SendThread线程和触发是在EventThread线程
服务端的注册和触发都是在请求处理链FinalRequestProcessor的processRequest方法内
网友评论