Zookeeper的Watch機(jī)制提供了服務(wù)通知功能,包含客戶端的注冊(cè)和觸發(fā)、服務(wù)端的注冊(cè)和觸發(fā)

Zookeeper客戶端:
通過(guò)ZKWatchManager管理
結(jié)構(gòu)都是存儲(chǔ)Map<String, Set<Watcher>>
例如:{"/abc",Set<Watcher>} 表示/abc節(jié)點(diǎn)下綁定了Watcher有哪些
1、臨時(shí)的一次性的,用完即刪除有dataWatches、existWatches、childWatches
2、永久性的有persistentWatches、persistentRecursiveWatches(表示遞歸的永久性Watcher)
Zookeeper服務(wù)端:
有dataWatches和childWatches
每個(gè)屬性下面都有watchTable、watch2Paths。
服務(wù)端的Watcher表示當(dāng)前客戶端的實(shí)現(xiàn)類NioServerCnxn
watchTable:Map<String, Set<Watcher>>
例如:{"/abc",Set<Watcher>} 表示/abc節(jié)點(diǎn)下有哪些客戶端含有Watcher
watch2Paths: Map<Watcher, Set<String>>
例如:{Watcher,Set<String>} 表示當(dāng)前客戶端下有那些路徑下有Watcher
客戶端注冊(cè)
當(dāng)調(diào)用自定義getData方法時(shí)

1、getData
這里并沒(méi)有注冊(cè)
org.apache.zookeeper.ZooKeeper#getData

①、把watcher和clientPath封裝成DataWatchRegistration對(duì)象
org.apache.zookeeper.ZooKeeper.DataWatchRegistration#DataWatchRegistration

org.apache.zookeeper.ZooKeeper.WatchRegistration#WatchRegistration

②、提交請(qǐng)求submitRequest
把封裝的對(duì)象使用cnxn.submitRequest方法提交請(qǐng)求,最終把DataWatchRegistration wcb賦值給數(shù)據(jù)包Packet的watchRegistration屬性

2、通過(guò)SendThread線程的readResponse讀取響應(yīng)數(shù)據(jù)
①、readResponse
org.apache.zookeeper.ClientCnxn.SendThread#readResponse

②、finishPacket
org.apache.zookeeper.ClientCnxn#finishPacket

③、register
通過(guò)數(shù)據(jù)包的WatchRegistration屬性值注冊(cè)watch
org.apache.zookeeper.ZooKeeper.WatchRegistration#register

org.apache.zookeeper.ZooKeeper.DataWatchRegistration#getWatches
獲取的是ZKWatchManager#dataWatches屬性

最終把當(dāng)前watch注冊(cè)給了ZKWatchManager的dataWatches屬性值。
服務(wù)端注冊(cè)
服務(wù)端的注冊(cè)是在請(qǐng)求處理鏈的最后一個(gè)請(qǐng)求處理類FinalRequestProcessor的processRequest方法來(lái)調(diào)用的
客戶端是以zooKeeper.getData方法來(lái)測(cè)試的,所以直接到getData
org.apache.zookeeper.server.FinalRequestProcessor#processRequest

org.apache.zookeeper.server.FinalRequestProcessor#handleGetDataRequest

org.apache.zookeeper.server.ZKDatabase#getData

org.apache.zookeeper.server.DataTree#getData

org.apache.zookeeper.server.watch.WatchManager#addWatch

org.apache.zookeeper.server.watch.WatchManager#addWatch
①、watchTable
Map<String, Set<Watcher>> 表示一個(gè)路徑有多少個(gè)客戶端Watcher(NioServerCnxn對(duì)象)

②、watch2Paths
Map<Watcher, Set<String>> 表示一個(gè)客戶端Watcher(NioServerCnxn對(duì)象)有多少個(gè)路徑

dataWatches和childWatches都是添加watchTable、watch2Paths的屬性值
dataWatches和childWatches是在DataTree創(chuàng)建對(duì)象時(shí)創(chuàng)建的,都屬于IWatchManager對(duì)象

客戶端觸發(fā)
是在SendThread線程讀取響應(yīng)readResponse時(shí)
1、添加到waitingEvents隊(duì)列
NOTIFICATION_XID = -1 是觸發(fā)WATCHER_EVENT
①、readResponse
org.apache.zookeeper.ClientCnxn.SendThread#readResponse


②、queueEvent

org.apache.zookeeper.ClientCnxn.EventThread#queueEvent

把當(dāng)前響應(yīng)事件添加到waitingEvents隊(duì)列中
2、一次性Watch與持久Watch的區(qū)別
在添加waitingEvents隊(duì)列之前,調(diào)用materialize方法根據(jù)路徑查找客戶端的Watch
org.apache.zookeeper.ZKWatchManager#materialize


dataWatches把當(dāng)前節(jié)點(diǎn)的watch給移除了,這就是為什么zooKeeper.getData方法的watch只能調(diào)用一次
而持久化節(jié)點(diǎn)并沒(méi)有移除

3、調(diào)用watch的process方法
便可以調(diào)用到客戶端自定義的監(jiān)聽(tīng)watch的process方法
org.apache.zookeeper.ClientCnxn.EventThread#run

org.apache.zookeeper.ClientCnxn.EventThread#processEvent

服務(wù)端觸發(fā)
服務(wù)端的觸發(fā)也是在請(qǐng)求處理鏈的最后一個(gè)請(qǐng)求處理類FinalRequestProcessor的processRequest方法來(lái)調(diào)用的
1、processRequest
org.apache.zookeeper.server.FinalRequestProcessor#processRequest

2、applyRequest
org.apache.zookeeper.server.FinalRequestProcessor#applyRequest

3、processTxn
org.apache.zookeeper.server.ZooKeeperServer#processTxn(org.apache.zookeeper.server.Request)

4、processTxnInDB
org.apache.zookeeper.server.ZooKeeperServer#processTxnInDB

5、processTxn
org.apache.zookeeper.server.ZKDatabase#processTxn

org.apache.zookeeper.server.DataTree#processTxn

org.apache.zookeeper.server.DataTree#processTxn

org.apache.zookeeper.server.DataTree#processTxn

6、setData
這里還是以/abc節(jié)點(diǎn)為例,開(kāi)啟一個(gè)客戶端修改/abc的值。調(diào)用命令set /abc xxx。直接到setData
這里可以看到修改前、修改后的數(shù)據(jù)、路徑、zxid等信息
org.apache.zookeeper.server.DataTree#setData

觸發(fā)NodeDataChanged事件類型

7、triggerWatch
org.apache.zookeeper.server.watch.WatchManager#triggerWatch

org.apache.zookeeper.server.watch.WatchManager#triggerWatch
可以看到當(dāng)前路徑(/abc)下的上下文客戶端對(duì)象

org.apache.zookeeper.server.watch.WatchManager#triggerWatch
這里調(diào)用了遞歸的文件,最終的效果就是查找當(dāng)前路徑下的客戶端對(duì)象


遍歷Watch對(duì)象的process方法

8、process
org.apache.zookeeper.server.NIOServerCnxn#process

響應(yīng)頭設(shè)置ClientCnxn.NOTIFICATION_XID,這里與客戶端讀取響應(yīng)頭相對(duì)應(yīng)
9、sendResponse
發(fā)送響應(yīng)數(shù)據(jù)到客戶端
org.apache.zookeeper.server.NIOServerCnxn#sendResponse

EventType事件類型
None、NodeCreated(節(jié)點(diǎn)創(chuàng)建)、NodeDeleted(節(jié)點(diǎn)刪除)、NodeDataChanged(節(jié)點(diǎn)數(shù)據(jù)修改)、NodeChildrenChanged(孩子節(jié)點(diǎn)改變)、DataWatchRemoved(監(jiān)聽(tīng)器Watch移除)、ChildWatchRemoved(孩子監(jiān)聽(tīng)器Watch移除)、PersistentWatchRemoved(持久化的監(jiān)聽(tīng)器Watch移除)

總結(jié):
根據(jù)客戶端使用不同的API觸發(fā)不同的監(jiān)聽(tīng)器Watch。
客戶端的注冊(cè)是在SendThread線程和觸發(fā)是在EventThread線程
服務(wù)端的注冊(cè)和觸發(fā)都是在請(qǐng)求處理鏈FinalRequestProcessor的processRequest方法內(nèi)