Zookeeper Watcher機制

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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章