本文先介紹zookeeper的znode、zookeeper的讀寫流程和zookeeper的節點類型,然后再根據zookeeper的節點類型和watch機制,圖解zookeeper的典型應用場景。
zookeeper簡介
zookeeper是分布式系統設計的基石,在分布式系統中扮演著“協調器”的作用(a distributed co-ordination service)。zookeeper中的數據是基于樹形結構組織的。如下圖所示,
zookeeper中的每個節點稱為一個znode,每個znode維持一個數據結構,其內容如下:
- Version number ? 版本號,當和該znode節點關聯的數據發生變化時,版本號會自增1。
- Action Control List (ACL) ? 訪問控制列表,znode的訪問控制機制,它控制znode的所有讀寫操作。
- Timestamp ? znode創建時的時間戳,精確到毫秒。
- Data length ? znode節點存儲數據的長度,最大1MB。
zookeeper的讀寫流程
當Client向zookeeper發出讀請求時,無論是Leader還是Follower,都直接返回查詢結果。
zookeeper寫入操作分為兩種情況,① 寫入請求直接發送到leader節點,② 寫入請求發送到Follower節點,這兩種情況有略微的區別。
寫入請求直接發送到Leader節點時的操作步驟如下:
- ① Client向Leader發出寫請求。
- ② Leader將數據寫入到本節點,并將數據發送到所有的Follower節點;
- ③ 等待Follower節點返回;
- ④ 當Leader接收到一半以上節點(包含自己)返回寫成功的信息之后,返回寫入成功消息給client;
寫入請求發送到Follower節點的操作步驟如下:
- ① Client向Follower發出寫請求。
- ② Follower節點將請求轉發給Leader;
- ③ Leader將數據寫入到本節點,并將數據發送到所有的Follower節點;
- ④ 等待Follower節點返回;
- ⑤ 當Leader接收到一半以上節點(包含自己)返回寫成功的信息之后,返回寫入成功消息給原來的Follower;
- ⑥ 原來的Follower返回寫入成功消息給Client;
zookeeper的節點類型
學習zookeeper的節點類型、watch機制是理解zookeeper的應用場景的前提。znode有三種基本類型和兩種組合類型:
- Persistence znode 持久znode,當創建之后會被持久化到存儲中。若不特別指定,創建的znode默認為持久型。
- Ephemeral znode ? 臨時znode,由客戶端在連接時創建,當客戶端斷開之后,該znode將會被自動刪除。同時,臨時znode不允許有子節點。
- Sequential znode ? 序列znode,當節點被創建時,zookeeper會自動在節點后面添加數字后綴,如創建一個以/app/session的序列znode,創建完成之后的名稱為/app/session0000000001。
- Persistence+Sequential znode,組合出的節點類型。
- Ephemeral+Sequential znode,組合出的節點類型。
zookeeper的watch機制
ZooKeeper 允許客戶端向服務端注冊一個 Watcher 監聽,當服務端的一些指定事件觸發了這個 Watcher,那么就會向指定客戶端發送一個事件通知來實現分布式的通知功能。
ZooKeeper 的 Watcher 機制主要包括客戶端線程、客戶端 WatchManager 和 ZooKeeper 服務器三部分。在具體工作流程上,簡單地講,客戶端在向 ZooKeeper 服務器注冊 Watcher 的同時,會將 Watcher 對象存儲在客戶端的 WatchManager 中。當 ZooKeeper 服務器端觸發 Watcher 事件后,會向客戶端發送通知,客戶端線程從 WatchManager 中取出對應的 Watcher 對象來執行回調邏輯。
zookeeper的應用場景
zookeeper實現配置中心
應用程序啟動時向zookeeper中和自己相關的znode上注冊一個watcher,當被監控znode中數據的變化時,應用程序會接收到zookeeper通知消息,這樣應用程序就可以根據獲得的通知消息對應用程序的配置進行修改。
注意:應用程序注冊的watcher每次只能觸發一次,當獲取完消息之后需要再注冊一次watcher
服務注冊中心
操作流程:
- ① 服務提供者向Zookeeper注冊服務;
- ② 服務消費者從zookeeper中查詢服務和監聽服務的變化;
- ③ 緩存服務信息到本地;
- ④ 調用遠端的服務;
zookeeper集群系統管理
客戶端通過zookeeper向集群發送控制命令,集群中的Node上運行一個Agent或者應用程序中運行一個watch線程用于watch配置節點,當znode中的數據發生變化時,執行相依的動作。流程如下:
- ① client 寫入命令到zookeeper中的某個znode;
- ② Node上的Agent或者應用程序中的watch線程獲取到znode的變化,讀取命令,并執行相應動作;
- ③ 各個Node將執行的結果寫入到zookeeper;
- ④ 客戶端的watch線程watch到有結果寫入到znode,從znode中讀取數據,并顯示。
zookeeper實現分布式鎖
分布式鎖實現步驟如下:
- 當需要獲取鎖時,客戶端先連接到zookeeper,創建一個ephemeral+sequence類型的znode,創建的znode名稱形如:lock_0000000001,lock_0000000002,lock_0000000003等。
- 然后,客戶端獲取/app/lock下的znode列表,判斷自己創建的znode是否序列最小,若是,則獲的鎖;若不是,在距離自己最近的前一個znode上設置一個watch,當獲取到znode變更通知后,重復此步驟直至獲得鎖;
- 執行業務代碼;
- 完成業務流程后,刪除對應的znode釋放鎖。
zookeeper集群選主
使用zookeeper的ephemeral+sequence類型的znode可以實現集群的選主功能。假設集群中有三個節點,大致的選主過程如下:
- 各個Node啟動之后,以“/app/leader_election/guid_”為路徑在zookeeper中創建ephemeral+sequence類型的znode,創建完成之后路徑“/app/leader_election/”下將有guid_0000000001,guid_0000000002,guid_0000000003三個節點。
- 每個Follower節點各自watch一個距離自己最近且比自己sequence小的znode;
- 各個Node之間將選擇序列最小的節點作為主節點,此時創建guid_0000000001序列的節點將被選舉為leader,其他Node變為Follower,如下圖所示。
當Leader宕機時,guid_0000000001將被刪除,Follower(Node2)watch到leader已被移除,且沒有比自己序列更小的Node了,于是將自己選為leader,其他Follower節點尋找Sequence最小的節點作為自己的主節點,選主完成后,如下圖所示。
當Node1恢復之后,會創建一個名稱為guid_0000000004的znode,并watch鄰近的znode,即guid_0000000003,如下圖所示:
個人理解:對應簡單的系統,使用zookeeper來實現分布式系統的選主還是比較便捷的,但是對于復雜一些的系統,或者實時性要求較高的分布式系統,一般會選擇基于Paxos或者Raft協議來實現選主功能。zookeeper本身也就是Paxos的一個實現。