ZooKeeper是用來協調(同步)分佈式進程的服務,提供了一個簡單高性能的協調內核,用戶能夠在此之上構建更多複雜的分佈式協調功能。算法
多個分佈式進程經過ZooKeeper提供的API來操做共享的ZooKeeper內存數據對象ZNode來達成某種一致的行爲或結果,這種模式本質上是基於狀態共享的併發模型,與Java的多線程併發模型一致,他們的線程或進程都是」共享式內存通訊「。Java沒有直接提供某種響應式通知接口來監控某個對象狀態的變化,只能要麼浪費CPU時間毫無響應式的輪詢重試,或基於Java提供的某種主動通知(Notif)機制(內置隊列)來響應狀態變化,但這種機制是須要循環阻塞調用。而ZooKeeper實現這些分佈式進程的狀態(ZNode的Data、Children)共享時,基於性能的考慮採用了相似的異步非阻塞的主動通知模式即Watch機制,使得分佈式進程之間的「共享狀態通訊」更加實時高效,其實這也是ZooKeeper的主要任務決定的—協調。Consul雖然也實現了Watch機制,但它是阻塞的長輪詢。服務器
從某種角度來講,能夠這樣對比(我的見解,能夠討論),ZooKeeper對等於JVM,ZooKeeper包含狀態對象(ZNode)和分佈式進程的底層執行引擎Zab,而JVM內部包含堆(多線程共享的大量對象存放區域)和多線程執行正確性約束規範JMM(Java內存模型),JMM確保了多線程的執行順序是正確的。Zab協議使得ZooKeeper的內部修改狀態操做直接是有序串行的,而JVM內部則是亂序並行的,須要添加額外的機制才能保證時序(內存屏障、處理器原子指令),而狀態讀取時,JVM和ZooKeeper都存在直接讀取時讀到舊數據,但ZooKeeper有Watch機制使得響應式讀取更高效,而JVM只能使用底層的內存屏障刷新共享狀態,以便其餘線程再次讀取時得到正確的新數據。數據結構
ZooKeeper提供的接口使得全部的分佈式進程的執行都是異步非阻塞的(WaitFree算法),內部是基於Version的CAS操做,而JVM提供了阻塞的和非阻塞的多種接口,有Synchronized、Volatile、AtomicOperations。基於接口之上構建線程或分佈式進程之間更復雜的同步或協調功能時,Java併發庫直接提供了閉鎖、循環柵欄、信號量等同步工具以及基礎的抽象隊列同步器,而ZooKeeper則須要用戶基於接口自行構建各類分佈式協調功能(分佈式鎖、分佈式發佈訂閱、集羣成員關係管理)。以下圖:多線程
ZooKeeper
|
JVM
|
|
共享狀態對象
|
ZNode | 堆中對象 |
底層執行模式
|
Zab順序執行 | 多處理器併發執行(內存屏障、原子機器指令) |
API接口
|
Get、Watch_Get、Cas_Set、Exist | Synchronized、volatile、final、Atomic |
協調或同步功能
|
分佈式發佈訂閱、鎖、讀寫鎖 | 併發庫同步工具、基於抽象隊列同步器構建的同步組件 |
Watch的總體流程以下圖所示,客戶端先向ZooKeeper服務端成功註冊想要監聽的節點狀態,同時客戶端本地會存儲該監聽器相關的信息在WatchManager中,當ZooKeeper服務端監聽的數據狀態發生變化時,ZooKeeper就會主動通知發送相應事件信息給相關會話客戶端,客戶端就會在本地響應式的回調相關Watcher的Handler。架構
如上圖所示,Watch被設計成一個接口,任何實現了Watcher接口的類就是一個新的Watcher,Watcher內部包含2個枚舉類,一個KeeperState,表示當事件發生時ZooKeeper的狀態,另外一個是事件發生的類型,主要分爲2類(一類是ZNode內容的變化,另外一類是ZNode子節點的變化),具體的描述見下表。併發
KeeperState
|
EventType
|
TriggerCondition
|
EnableCalls
|
Desc
|
SyncConnected異步 (3)分佈式
|
None (-1)工具 |
客戶端與服務器成功創建會話 | 此時客戶端與服務器處於鏈接狀態 | |
同上 | NodeCreated (1)post |
Watcher監聽的對應數據節點被建立 | Exists | 同上 |
同上 | NodeDeleted (2) |
Watcher監聽的對應數據節點被刪除 | Exists, GetData, and GetChildren | 同上 |
同上 | NodeDataChanged (3) |
Watcher監聽的數據節點的數據內容和數據版本號發生變化 | Exists and GetData | 同上 |
同上 | NodeChildrenChanged (4) |
Watcher監聽的數據節點的子節點列表發生變化,子節點內容變化不會觸發 | GetChildren | 同上 |
Disconnected (0) |
None (-1) |
客戶端與ZooKeeper服務器斷開鏈接 | 此時客戶端與服務器處於斷開鏈接的狀態 | |
Expried (-112) |
None (-1) |
會話超時 | 此時客戶端會話失效,一般同時也會收到SessionExpiredException異常 | |
AuthFailed (4) |
None (-1) |
一般有兩種狀況: 1.使用錯誤的scheme進行權限檢查 2.SASL權限檢查失敗 |
收到AuthFailedException異常 |
如上圖所示,WatchEvent有2種表示模式,一種是邏輯表示即WatchedEvent,是直接封裝了各類抽象的邏輯狀態(KeeperState,EventType),適用於客戶端和服務端各自內部處理,另外一種是物理表示即封裝的更可能是底層基礎的傳輸數據結構(int,String),而且實現了序列化接口,主要用來作底層的數據傳輸。