ZooKeeper 監視點詳解

應用程序須要知道 ZooKeeper 節點狀態的狀況很常見,如主從模式中從節點須要知道主節點是否崩潰。java

輪訓的方式很低效,尤爲是在指望變化頻率很低時。node

ZooKeeper 提供了監視點(watch)這一處理節點變化的重要機制,客戶端對指定 znode 節點註冊通知請求,在節點狀態發生變化時就會收到一個單次的通知。服務器

單次通知

監視點是一個單次的觸發器,應用對某個節點註冊了一個監視點,會在該節點第一次觸發事件時嚮應用發送通知。異步

監視點與會話關聯,當客戶端與一臺 ZooKeeper 服務器斷開鏈接後,會鏈接到集合中另外一臺服務器,同時將未觸發的監視列表發送到服務器。若是所監視的節點已經發生變化,會向客戶端發送通知,不然會在新的服務器上註冊監視點。性能

單次觸發的通知可能會丟失事件,好比在收到通知、註冊新的監視點之間的這段時間所發生的事件。不過丟失事件一般不會形成影響,節點變化能夠經過註冊新監視點時讀取節點狀態來捕獲。spa

設置監視點

ZooKeeper 中的全部讀操做都可設置監視點,包括 getData、getChildren 和 exists。code

使用監視點須要實現 Watcher 接口,重寫 void process(WatchedEvent event) 方法。其中 WatchedEvent 包含 ZooKeeper 的會話狀態(KeeperState)和事件類型(EventType)。對象

事件類型包括 NodeCreated、NodeDeleted、NodeDataChanged、NodeChildrenChanged 和 None。前 3 個類型涉及單個 znode 節點,第 4 個即 NodeChildrenChanged 涉及所監視節點的子節點。None 表示 ZooKeeper 會話狀態發生變化。blog

事件類型 設置監視點方式
NodeCreated 經過 exists 調用設置監視點
NodeDeleted 經過 exists 或 getData 調用設置監視點
NodeDataChanged 經過 exists 或 getData 調用設置監視點
NodeChildrenChanged 經過 getChildren 調用設置監視點

原子操做

ZooKeeper 在 3.4.0 版本添加了原子操做的特性,經過 multiop 能夠原子性地執行多個 ZooKeeper 操做,這些操做要麼所有成功,要麼所有失敗。接口

使用 multiop 時,首先建立出包含 ZooKeeper 操做的 Op 對象,再把多個 Op 對象放到 Iterable 類型的對象中供客戶端調用,如:

Op deleteZnode(String path) {
    return Op.delete(path, -1);
}

Op createZnode(String path, String data) {
    return Op.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}

List<OpResult> results = zk.multi(Arrays.asList(deleteZnode("/a"), createZnode("b", "data")));
複製代碼

multi 方法一樣有異步版本,同步和異步方法的定義以下:

public List<OpResult> multi(Iterable<Op> ops) throw InterruptedException,KeeperException;
public void multi(Iterable<Op> ops, MultiCallback callback, Object context);
複製代碼

Transaction 封裝 multi 方法,提供了更簡單的調用方式,能夠經過建立 Transaction 對象來添加操做、提交事務。

List<OpResult> results = zk.transaction().delete("/a", -1).create("/b", "data".getBytes()
        , ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT).commit();
複製代碼

multi 提供的另外一個功能是檢查 znode 的版本號,在輸入的 znode 節點與版本號不匹配時,multi 會調用失敗。

public static Op check(String path, int version);
複製代碼

消除隱藏通道

ZooKeeper 的狀態會在全部服務器間互相複製,服務端對狀態變化的順序會達成一致,並使用相同的順序對狀態進行更新。但事實上服務端不多同時執行更新操做,若是經過隱藏通道進行通訊可能會出現狀態不一致的現象。

好比 Client-1 鏈接到 Server-1,Client-2 鏈接到 Server-2,/z 節點中的數據由 a 變爲 b。

Server-1 更新狀態會向 Client-1 發送通知,Client-1 收到通知後向 Client-2 發送 /z 節點變化的消息,Client-2 再經過 Server-2 進行操做,若是此時 Server-2 尚未對 /z 節點的狀態進行更新,就會讀取到過時的數據。

Client-1 向 Clinet-2 發送消息,就是隱藏的通道,會致使錯誤發生,正確的作法是 Client-2 只經過 ZooKeeper 服務端接收消息,消除隱藏通道。

避免在同一個節點設置過多監視點

當節點發生變化時,會向設置監視點的客戶端發送消息,若是對同一節點設置過多的監控點,就會在節點狀態變動時出現發送消息的高峯,可能會對性能形成影響。

條件容許的話,最好對節點設置少許的監視點,理想狀況下只設置一個。

好比多個客戶端爭相獲取一個鎖:能夠一個客戶端經過建立臨時節點獲取鎖,其餘客戶端對這個節點設置監視點;也能夠改成讓客戶端建立有序臨時節點,最小序號的客戶端獲取鎖,其餘客戶端只對比本身序號小 1 的節點設置監視點。

一個監視點會佔用服務端約 300 字節的內存,開發時須要注意監視點數量。

原文地址

相關文章
相關標籤/搜索