ZooKeeper 的五種經典應用場景
這篇文章,我們聊聊 ZooKeeper 的五種經典應用場景 。
圖片
一、Distributed Configuration 配置管理
我們在項目開發維護過程中會有很多公共變量或資源,需要統一管理,以前可以把它們寫在程序公共類或者配置文件中,可是這樣以后有變動,程序就需要重新部署,很是不方便,而且分布式、微服務等技術出現,修改維護多個項目管理也變得復雜。
為了解決以上問題,實現一次打包多地部署需求,減少項目管理及安全風險,我們需要將可變變量外移并通過頁面統一可視化管理,基于此,很多大廠技術團隊都建設了配置中心。
下圖是配置中心的通用設計:
圖片
ZooKeeper 天然具備作為配置中心的特性,表現在如下兩點:
1.配置數據存儲到 ZooKeeper 的節點中 ;
2、應用注冊改該節點的 watch 監聽,如果一旦這個節點數據發生變更,那么所有訂閱該節點的客戶端都可以獲取數據變更通知。
圖片
1.發布者作為 Zookeeper 客戶端首先在 zk 中創建一個節點,該節點的數據內容即為當前應用服務 A 的配置文件。
2.應用服務 A 在啟動時從 zk 的節點上讀取數據內容,即獲取配置文件內容。
3.讀取數據內容后,應用服務 A 會向 zk 的該節點注冊一個數據內容變更的 watcher 監聽。
4.當發布者更新配置內容并將其更新到 zk 的對應節點數據內容上時,zk 會觸發相應的 watcher 事件,并向每一個應用 A 推送此事件通知。
5.應用 A 接收到 watcher 事件后,會觸發本地的 watcher 回調方法,該方法將從 zk 中重新拉取節點的數據內容,即獲取更新后的配置內容。
二、Service Discovery 服務發現
大規模服務化之后,服務越來越多,服務消費者在調用服務提供者的服務時,需要在配置文件中維護服務提供者的URL地址,當服務提供者出現故障或者動態擴容時,所有相關的服務消費者都需要更新本地配置的URL地址,維護程本很高。
這時,實現服務的上下線動態感知及服務地址的動態維護就顯得非常重要了。
圖片
上圖是我們熟知的 RPC 框架 Dubbo 的架構 ,服務生產者啟動之后,會將服務注冊到注冊中心 ,服務消費者會通過注冊中心訂閱相關服務, 服務消費者獲取到服務路由信息后,調用服務生產者提供的服務。
zookeeper 提供了一種針對 Znode 的訂閱/通知機制,就是當 Znode 節點狀態發生變化或者 zookeeper 客戶端連接狀態發生變化時,會觸發事件通知;這個機制在服務注冊與發現中,對服務調用者及時感知服務提供者的變化提供了非常好的解決方案。
圖片
流程:
- 服務提供者啟動時: 向
/dubbo/com.foo.BarService/providers目錄下寫入自己的 URL 地址。 - 服務消費者啟動時: 訂閱
/dubbo/com.foo.BarService/providers目錄下的提供者 URL 地址。并向/dubbo/com.foo.BarService/consumers目錄下寫入自己的 URL 地址 - 監控中心啟動時: 訂閱
/dubbo/com.foo.BarService目錄下的所有提供者和消費者 URL 地址。
三、Master / Slave 解決單點故障問題
分布式架構中, 為了保證服務的可用性,通常會采用集群模式:其中一個機器宕機后,集群中的其他節點會接替故障節點繼續工作。
這種場景中,需要從集群中選擇一個節點作為 Master 節點,剩余的其他節點作為備份節點隨時待命;當原有的 Master 節點出現故障之后,還需要從備份節點中選擇一個節點作為 Master 節點繼續服務。
實現 Master選舉 有兩種方式:
1.爭搶臨時節點
使用 zk 的 master 選舉是利用了 zk 中多個客戶端對同一節點創建時,只有一個客戶端可以成功的特性實現。
圖片
具體來說,由三步完成:
1)多個客戶端同時發起對同一臨時節點 /master-election/master 進行創建的請求,最終只能有一個客戶端成功。這個成功的客戶端主機就是 Master,其它客戶端就是 Slave。
2)Slave 都向這個臨時節點的父節點 /master-election 注冊一個子節點列表的 watcher 監聽。
3)一旦該 Master 宕機,臨時節點就會消失,zk 服務器就會向所有 Slave 發送子節點變更事件,Slave 在接收到事件后會調用相應的回調方法,該回調方法會重新向這個父節點創建相應的臨時子節點。誰創建成功,誰就是新的 Master。
2.創建臨時有序節點
圖片
在設計 ZooKeeper 實現的 Master 選舉機制時,考慮到當無法實現完全的 Active-Active 模式(即所有節點同時處理讀寫請求)的情況下,可以采用以下策略:
- 父節點類型為 Persistent: 創建一個持久化的父節點作為服務的基礎路徑。這個父節點不會因為創建它的客戶端會話的結束而被刪除,確保了即使創建者離線,其他參與者仍然能夠找到并使用該節點。
- 子節點類型為 Ephemeral + Sequential: 每個參與選舉的服務實例在其啟動時,在上述持久化父節點下創建一個臨時且順序的子節點。這些臨時節點的存在依賴于創建它們的客戶端會話;如果客戶端會話終止,相應的節點將自動被移除。順序特性則賦予每個新創建的節點一個唯一的遞增編號,這有助于確定哪個節點是最早的參與者。
- Master 選擇邏輯: 在所有的子節點中,序列號最小的那個節點所對應的客戶端會被選為 Master,其余的客戶端則被視為 Slave。
- Slave 監控機制: 每個 Slave 客戶端都會監控比自己序列號小的最大子節點的
NodeDeleted事件。這意味著每一個 Slave 都在等待前一位(按創建時間排序)的 Slave 或 Master 的失效信號。 - Master 晉升規則: 當某個 Slave 接收到它正在監聽的節點被刪除的通知(即
NodeDeleted事件觸發),它會檢查是否有更早創建的節點仍然存在。如果沒有,則該 Slave 自動晉升為新的 Master,承擔起協調者的職責。如果有更早的節點存在,則更新監聽目標至下一個較早的節點。
4.Sequence Generation 序列產生器

核心流程如下:
1)創建持久節點(persistent node)ZooKeeper 中創建一個持久節點。這個節點用來保存序列號的信息。
2)設置數據版本號為 -1 ,告訴 ZooKeeper 不要進行版本號校驗,客戶端總會調用成功,ZooKeeper 會將該節點的數據版本號自動 + 1 。
3)客戶端會得到最新的數據版本號,作為序列號使用。由于 ZooKeeper 的版本號是遞增的,所以每次調用都會得到一個新的唯一序列號。
該方案的優點是一次調用即可分配序列號,缺點是受限于數據版本號,只能分配 32 位序列號。
偽代碼如下:

五、Distributed Locking Service 分布式鎖
分布式鎖是控制分布式系統同步訪問共享資源的一種方式。ZooKeeper 可以實現分布式鎖的功能。
圖片
核心流程如下:
- 創建持久節點(Persistent Node)作為鎖對象:
- 首先,在 ZooKeeper 中為每個鎖創建一個持久的(persistent)節點。這相當于給每種類型的鎖分配一個唯一的路徑。
- 客戶端申請鎖:
- 客戶端為了獲取鎖,會在該鎖對應的持久節點下創建一個臨時順序(ephemeral and sequential)子節點。例如,如果鎖對象節點是 /Locks/Lock1/Lock,那么客戶端可能會創建類似 /locks/Lock1/Lock0000000001 的節點。
- 檢查序列號:
- 創建完子節點后,客戶端會獲取鎖對象節點下的所有子節點列表,并按序號排序。
- 如果客戶端發現它所創建的子節點擁有最小的序列號,那么它就獲得了鎖。
- 釋放鎖:
- 擁有鎖的客戶端在完成任務后可以通過刪除自己的子節點來釋放鎖。由于節點是臨時的(ephemeral),所以即使客戶端崩潰,ZooKeeper 也會自動刪除該節點,從而避免死鎖。
- 等待鎖:
- 如果客戶端沒有獲得鎖(即它的子節點不是序列號最小的),它應該監聽比自己序列號小的最近一個子節點的
NodeDeleted事件。 - 一旦這個更早的子節點被刪除(意味著持有鎖的客戶端已經完成了工作并釋放了鎖),當前客戶端將收到通知,然后重新檢查它是否現在可以獲取鎖。
























