Zookeeper作為分布式系統的底層協調服務有着其簡單可依靠的數據模型,數據模型加之數據同步、一致性處理和可靠性,在此之上有很多經典的應用,例如,分布式鎖、服務器動態上線下感知、主節點選舉、數據發布與訂閱、負載均衡等等。雖然應用場景很多,但是最根本的還是基於兩個核心的服務,1.管理和存儲數據結點,2.提供對結點的監聽服務。
一.Zookeeper數據模型
Zookeeper數據模型類似Linux操作系統的文件系統,也是以樹的形式來存儲。嚴格來說是一顆多叉樹,每個節點上都可以存儲數據,每個節點還可以擁有N個子結點,最上層是根節點以“/”來代表。
在每個結點上都存儲了相應的數據,數據可以是字符串、二進制數。但是默認情況下每個結點的數據大小的上限是1M,這是因為Zookeeper主要是用來協調服務的,而不是存儲數據,管理一些配置文件和應用列表之類的數據。雖然可以修改配置文件來改變數據大小的上限,但是為了服務的高效和穩定,建議結點數據不要超過默認值。
可以看到,在Zookeeper中存儲的創建的結點和存儲的數據包含結點的創建時間、修改時間、結點id、結點中存儲數據的版本、權限版本、孩子結點的個數、數據的長度等信息。在創建結點的時候還可以選擇臨時結點、序列化節點等類型,這在應用時就非常方便了。在后面的應用中會有所體現。
Zookeeper提供了兩種客戶端,命令行客戶端和API客戶端,關於命令行客戶端的使用可以help一下。
二.Zookeeper典型應用示例
1.分布式共享鎖
需求描述:在一個分布式系統中,所有服務器結點共享一種資源,為了保證數據的一致性和准確性就必須對共享資源做出訪問限制。因為不在一台機器上所以不能使用並發鎖來同步。需要Zookeeper做分布式協調服務。
設計思路:在Zookeeper上創建一個鎖結點,然后每個服務器如果需要訪問共享資源那么就在鎖結點下創建一個鎖結點的孩子結點,這里需要注意的是,孩子結點需要創建成為臨時序列結點,這樣一來如果某個服務器在擁有鎖的時候掛了,其擁有的鎖會自動釋放。序列結點會使所有的鎖都可以有序。在獲取鎖的時候創建監聽該鎖上一個鎖的刪除事件,這樣可以避免“羊群效應”,在一個鎖結點被釋放(刪除)時不會喚醒所有在等待的鎖結點,可以節約網絡和服務資源。
這只是一個基本的實現思路,具體的規則就是序列最小的獲取鎖,優先級是按照時間來算的,但是基本思路都是一樣的,可以基於此上修改成為優先級可調節的,或者改成分布式讀寫鎖提高訪問性能。
分布式共享鎖的實現請參考:https://github.com/wxisme/zoopack/blob/master/zoopack/src/main/java/org/zoopack/lock/
2.服務器動態上下線感知
需求描述:在分布式系統中,可能有很多個結點,在提供服務的過程中可能會有服務器異常的或者正常的下線、掛掉或者修復之后上線繼續提供服務,那么為了提高系統的可靠性就需要實時的更新在線服務器列表,以便在分發請求的時候不會分發到已經下線的服務器中。那么實時更新服務器列表就可以使用Zookeeper的數據結點來存儲、用NodeChildrenChangedWatcher來監聽所有的服務器下線和上線的事件。
設計思路:在Zookeeper根目錄下創建一個服務器父節點,在這個父節點下可以有多個服務器子節點,在父節點上注冊一個NodeChildrenChangedWatcher來監聽子節點的增加和刪除事件,如果有服務器子節點增加或者刪除就會更新服務器列表。一般的就可以認為服務器列表就是實時更新、有效的。這樣在做請求分發或者負載均衡的時候就能夠做到穩定和正確。
關於服務器動態上下線動態感知的實現請參考:https://github.com/wxisme/zoopack/tree/master/zoopack/src/main/java/org/zoopack/perception
三.總結
通過以上兩個例子也可以看出來,不管Zookeeper應用場景再多,業務邏輯再復雜,只要抓住兩個核心可以了,1.管理和存儲數據結點(小數據量),2.提供對結點的監聽服務。只要合理的應用這兩個特性就可以很好的使用它,當然任何一個系統都不是簡單的一個技術可以完成的,在特定的業務場景下有特定的解決方案,在不同的應用環境和數據壓力下也要對Zookeeper及其上下游技術進行調優,這樣的話就需要對zk的配置文件和內部實現的算法,選舉算法、數據一致性算法等有一定的理解和實踐。