watcher流程
三個過程:
client 註冊 Watcher
server 處理 Watcher
client 回調 Watcher
client 註冊 Watcher
zk client 可以通過 new ZooKeeper(),getData(),getChildren(), exist() 傳入 watcher對象 來 註冊 Watcher.
比如對於 getData()時註冊watcher的操作
.實際上 client 就是把 watcher 對象 存到 DataWatchRegistration 裏,再創建 Packet,存入 outgoingQueue,等待 SendThread 線程取出來發給 server.
// 1.創建ZooKeeper對象時註冊Watcher
- new ZooKeeper()傳入Watcher,會回調Watcher.process()方法
- ZooKeeper中會創建ZKWatchManager
- watchManager.defaultWatcher = watcher;初始化默認Watcher
- 解析connectString創建hostProvider
- 創建客戶端連接管理類ClientCnxn
- 啓動ClientCnxn線程
- ClientCnxn構造方法中創建sendThread和eventThread
EventThread負責處理Server返回的WatchedEvent,回調註冊的客戶端事件接口處理函數
SendThread爲outgoing(傳出)請求隊列提供服務並生成心跳.它還會產生ReadThread
// 2.setData()
- zk.setData(path, data, version);
- 創建RequestHeader,調用cnxn.submitRequest(h, request, response, null);
- clientCnxn.submitRequest()中:將Request等信息封裝成packet,放入outgoingQueue隊列中
// sendThread邏輯
- ClientCnxn.SendThread中
- run()邏輯
- 建立與server的連接
- 定時發送ping
- 委託給 clientCnxnSocket.doTransport()進行底層的nio傳輸
- ClientCnxnSocketNIO.doIO()中
從outgoingQueue取出packet
- p.createBB();// 序列化
- sock.write(p.bb);// 發送
- 從outgoingQueue隊列中移除該發送的包
- pendingQueue.add(p);// 將packet加入到pendingQueue隊列
// 3.getData()時註冊watcher
- zk.getData(zooDataPath, watcher, stat);getData()時註冊watcher
- wcb = new DataWatchRegistration(watcher, clientPath); // 註冊 watcher到DataWatchRegistration中
- packet = new Packet(h, r, request, response, watchRegistration);通過watchRegistration創建packet,存入outgoingQueue,等待發送
server 處理 Watcher
比如對於 getData()時註冊watcher的處理:其實 server端就是在 FinalRequestProcessor 中將 Watcher(其實是NIOServerCnxn對象) 添加到 WatchManager 對象的 watchTable 和 watch2Paths 屬性中.
- 2.Server處理Watcher
- 處理客戶端發來的zk.getData()註冊watcher的請求
- NIOServerCnxnFactory.run()有讀寫事件
- NIOServerCnxn.doIO()
- NIOServerCnxn.readPayload();// 讀取內容
- NIOServerCnxn.readRequest()
- ZooKeeperServer.processPacket()
- ZooKeeperServer.submitRequest(si);// 提交請求
- touch(si.cnxn);// 判斷session是否存在或者已經超時
- firstProcessor.processRequest(si);// 處理請求
- Processor鏈處理...
- FinalRequestProcessor.processRequest()中
- case OpCode.getData:// 對於獲取數據請求,若有watch會註冊
- dataWatches.addWatch(path, watcher);// 若有watcher的話則註冊watcher,此時watcher爲NIOServerCnxn.將Watcher添加到WatchManager對象的watchTable和watch2Paths屬性中
當server端收到了client端的setData()請求,由於引起了數據變更,會觸發 getData()時註冊watcher. 其實就是從上面 getData() 時存入的 watchTable 裏查出來 watchers 並移除(只觸發一次),調用 watcher.process()方法,也就是 NIOServerCnxn.process() 發送給 客戶端 Watcher事件.
- 3.Server觸發Watcher
- 處理client發來的zk.setData()請求
- FinalRequestProcessor.processRequest()中
- rc = zks.processTxn(hdr, txn);// 處理事務,應用到 dataTree上
- DataTree.setData()中,有watcher時會觸發watcher
- dataWatches.triggerWatch(path, EventType.NodeDataChanged); // setData()時會導致數據變更,若有watcher會觸發watcher
- watchers = watchTable.remove(path); // 獲取該path對應的watchers,watchers只觸發一次就移除
- w.process(e);// 進行watcher事件處理,傳入WatchedEvent,此時的w是NioServerCnxn對象
- NIOServerCnxn.process()中,創建響應頭,xid=-1代表watcher事件.將WatchedEvent對象轉換爲WatcherEvent,用於網絡傳輸,響應客戶端
client 回調 Watcher
client端 通過 SendThread.readResponse() 接收服務端響應.
根據 replyHdr.xid 判斷是 Watcher事件.
將 來自服務端的響應,反序列成 WatcherEvent 對象,WatcherEvent 的信息裏只包含 type,state,path 信息.
然後再將 WatcherEvent 對象 轉成 WatchedEvent 對象.
從 ZKWatchManager.dataWatches 中 取出 path 對應的 watcher 並移除(只觸發一次),連同 WatchedEvent 創建WatcherSetEventPair 對象.入 waitingEvents 隊列,交給 eventThread 線程處理.
eventThread線程 從 waitingEvents 隊列裏取出對應的 watcher,調用 watcher.process() 處理.
- 4.Client回調Watcher
- SendThread.readResponse()接收服務端響應
- 根據replyHdr.getXid() == -1 判斷是Watcher通知,反序列化出WatcherEvent對象
- eventThread.queueEvent(we);// 將Watcher事件 入事件隊列,交給EventThread處理
- watcher.materialize()根據watcher的事件類型進行不同的處理
- 比如NodeDataChanged和NodeCreated事件
- 1.從 ZKWatchManager.dataWatches 中移除 clientPath 對應的 watchers(相當於只能觸發一次),並將這些移除的 watchers 添加到 result 中返回
- 通過watcher和event創建WatcherSetEventPair
- waitingEvents.add(pair);// 將WatcherSetEventPair對象入waitingEvents這個LinkedBlockingQueue中(保證watcher順序)
- EventThread.run()中
- 從waitingEvents中取出event,調processEvent(event)
- processEvent(event)中
- event中包含實際的watcher對象,直接回調watcher.process()即可
相關類
Watcher
WatchedEvent
ZooKeeper
ZKWatchManager
ClientWatchManager
WatchRegistration
ChildWatchRegistration
DataWatchRegistration
ExistsWatchRegistration
ClientCnxn
Packet
Watcher接口
Watcher 接口中包含:
1.Event 接口,用於定義事件所代表的狀態
Event 接口中包含 KeeperState(事件發生時zk的狀態
) 和 EventType(事件類型)兩個枚舉類
2.process() 抽象方法
package org.apache.zookeeper;
public interface Watcher {
/**
* 表示事件
*/
@InterfaceAudience.Public
public interface Event {
/**
* 事件發生時Zookeeper的狀態
*/
@InterfaceAudience.Public
public enum KeeperState {
@Deprecated
Unknown(-1),// 未知狀態.不再使用,服務器不會產生此狀態
Disconnected(0),// 斷開
@Deprecated
NoSyncConnected(1),// 未同步連接.不再使用,服務器不會產生此狀態
SyncConnected(3),// 同步連接狀態
AuthFailed(4),// 認證失敗狀態
ConnectedReadOnly(5),// 只讀連接狀態
SaslAuthenticated(6),// SASL認證通過狀態
Expired(-112);// 過期狀態
// ...
}
/**
* 事件類型
*/
public enum EventType {
None(-1),// 無
NodeCreated(1),// 節點創建
NodeDeleted(2),// 節點刪除
NodeDataChanged(3),// 節點數據變化
NodeChildrenChanged(4);// 節點的子節點變化
}
}
// process()方法
abstract public void process(WatchedEvent event);
}
回調方法 process()
zk server 向 client 發送一個 watcher 通知時,會回調對應的 process() 方法.
abstract public void process(WatchedEvent event);
WatchedEvent
process()方法的參數 WatchedEvent,zk server會將watcher事件通過 WatchedEvent 對象 傳遞給 client,WatchedEvent 包含了事件的一些屬性:
WatchedEvent屬性
-keeperState: KeeperState // 通知狀態
-eventType: EventType // 事件類型
-path: String // 對應的path
WatcherEvent
WatcherEvent 用於網絡傳輸.
zk server 在生成 WatchedEvent 事件後,通過 getwrapper() 將 WatchedEvent 包裝成 可序列化的 WatcherEvent事件,用於網絡傳輸.
zk client 接收到 server 的 WatcherEvent 對象後,會將 WatcherEvent 反序列化成 WatchedEvent 事件,傳給 process() 方法.
ServerCnxn
ServerCnxn 實現了 Watcher 接口
WatchManager
WatchManager 是 ZooKeeper 服務端 Watcher 的管理者,包含 watchTable 和 watch2Paths.