1. 程式人生 > >zookeeper(四):核心原理(Watcher、事件和狀態)

zookeeper(四):核心原理(Watcher、事件和狀態)

ted sso 兩個 reat exist 這一 通過 投票 children

zookeeper主要是為了統一分布式系統中各個節點的工作狀態,在資源沖突的情況下協調提供節點資源搶占,提供給每個節點了解整個集群所處狀態的途徑。這一切的實現都依賴於zookeeper中的事件監聽和通知機制

zookeeper中的事件和狀態

事件和狀態構成了zookeeper客戶端連接描述的兩個維度。註意,網上很多帖子都是在介紹zookeeper客戶端連接的事件,但是忽略了zookeeper客戶端狀態的變化也是要進行監聽和通知的。這裏我們通過下面的兩個表詳細介紹zookeeper中的事件和狀態(zookeeper API中被定義為@Deprecated的事件和狀態就不介紹了)。

zookeeper客戶端與zookeeper server連接的狀態

連接狀態狀態含義
KeeperState.Expired 客戶端和服務器在ticktime的時間周期內,是要發送心跳通知的。這是租約協議的一個實現。客戶端發送request,告訴服務器其上一個租約時間,服務器收到這個請求後,告訴客戶端其下一個租約時間是哪個時間點。當客戶端時間戳達到最後一個租約時間,而沒有收到服務器發來的任何新租約時間,即認為自己下線(此後客戶端會廢棄這次連接,並試圖重新建立連接)。這個過期狀態就是Expired狀態
KeeperState.Disconnected 就像上面那個狀態所述,當客戶端斷開一個連接(可能是租約期滿,也可能是客戶端主動斷開)這是客戶端和服務器的連接就是Disconnected狀態
KeeperState.SyncConnected 一旦客戶端和服務器的某一個節點建立連接(註意,雖然集群有多個節點,但是客戶端一次連接到一個節點就行了),並完成一次version、zxid的同步,這時的客戶端和服務器的連接狀態就是SyncConnected
KeeperState.AuthFailed zookeeper客戶端進行連接認證失敗時,發生該狀態

需要說明的是,這些狀態在觸發時,所記錄的事件類型都是:EventType.None

zookeeper中的watch事件(當zookeeper客戶端監聽某個znode節點”/node-x”時)

zookeeper事件事件含義
EventType.NodeCreated 當node-x這個節點被創建時,該事件被觸發
EventType.NodeChildrenChanged 當node-x這個節點的直接子節點被創建、被刪除、子節點數據發生變更時,該事件被觸發。
EventType.NodeDataChanged 當node-x這個節點的數據發生變更時,該事件被觸發
EventType.NodeDeleted 當node-x這個節點被刪除時,該事件被觸發。
EventType.None 當zookeeper客戶端的連接狀態發生變更時,即KeeperState.Expired、KeeperState.Disconnected、KeeperState.SyncConnected、KeeperState.AuthFailed狀態切換時,描述的事件類型為EventType.None

技術分享圖片

watch機制

Znode發生變化(Znode本身的增加,刪除,修改,以及子Znode的變化)可以通過Watch機制通知到客戶端。那麽要實現Watch,就必須實現org.apache.zookeeper.Watcher接口,並且將實現類的對象傳入到可以Watch的方法中。Zookeeper中所有讀操作(getData(),getChildren(),exists())都可以設置Watch選項。Watch事件具有one-time trigger(一次性觸發)的特性,如果Watch監視的Znode有變化,那麽就會通知設置該Watch的客戶端。

在上述說道的所有讀操作中,如果需要Watcher,我們可以自定義Watcher,如果是Boolean型變量,當為true時,則使用系統默認的Watcher,系統默認的Watcher是在Zookeeper的構造函數中定義的Watcher。參數中Watcher為空或者false,表示不啟用Wather。

watch特性1:一次性觸發器

客戶端在Znode設置了Watch時,如果Znode內容發生改變,那麽客戶端就會獲得Watch事件。例如:客戶端設置getData("/znode1", true)後,如果/znode1發生改變或者刪除,那麽客戶端就會得到一個/znode1的Watch事件,但是/znode1再次發生變化,那客戶端是無法收到Watch事件的,除非客戶端設置了新的Watch。

watch特性2:發送至客戶端

Watch事件是異步發送到Client。Zookeeper可以保證客戶端發送過去的更新順序是有序的。例如:某個Znode沒有設置watcher,那麽客戶端對這個Znode設置Watcher發送到集群之前,該客戶端是感知不到該Znode任何的改變情況的。換個角度來解釋:由於Watch有一次性觸發的特點,所以在服務器端沒有Watcher的情況下,Znode的任何變更就不會通知到客戶端。不過,即使某個Znode設置了Watcher,且在Znode有變化的情況下通知到了客戶端,但是在客戶端接收到這個變化事件,但是還沒有再次設置Watcher之前,如果其他客戶端對該Znode做了修改,這種情況下,Znode第二次的變化客戶端是無法收到通知的。這可能是由於網絡延遲或者是其他因素導致,所以我們使用Zookeeper不能期望能夠監控到節點每次的變化。Zookeeper只能保證最終的一致性,而無法保證強一致性。

watch特性3:設置watch的數據內容

Znode改變有很多種方式,例如:節點創建,節點刪除,節點改變,子節點改變等等。Zookeeper維護了兩個Watch列表,一個節點數據Watch列表,另一個是子節點Watch列表。getData()和exists()設置數據Watch,getChildren()設置子節點Watch。兩者選其一,可以讓我們根據不同的返回結果選擇不同的Watch方式,getData()和exists()返回節點的內容,getChildren()返回子節點列表。因此,setData()觸發內容Watch,create()觸發當前節點的內容Watch或者是其父節點的子節點Watch。delete()同時觸發父節點的子節點Watch和內容Watch,以及子節點的內容Watch。

Zookeeper Watcher的運行機制

1,Watch是輕量級的,其實就是本地JVM的Callback,服務器端只是存了是否有設置了Watcher的布爾類型。(源碼見:org.apache.zookeeper.server.FinalRequestProcessor)
2,在服務端,在FinalRequestProcessor處理對應的Znode操作時,會根據客戶端傳遞的watcher變量,添加到對應的ZKDatabase(org.apache.zookeeper.server.ZKDatabase)中進行持久化存儲,同時將自己NIOServerCnxn做為一個Watcher callback,監聽服務端事件變化
3,Leader通過投票通過了某次Znode變化的請求後,然後通知對應的Follower,Follower根據自己內存中的zkDataBase信息,發送notification信息給zookeeper客戶端。
4,Zookeeper客戶端接收到notification信息後,找到對應變化path的watcher列表,挨個進行觸發回調。

流程圖

技術分享圖片

zookeeper(四):核心原理(Watcher、事件和狀態)