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的配置檔案和內部實現的演算法,選舉演算法、資料一致性演算法等有一定的理解和實踐。