ZooKeeper概念與應用

發表於2018-09-24

Zookeeper是開源的分散式協調服務,提供了分散式資料一致性的解決方案。

Zookeeper 可用作配置中心和分散式鎖服務,在 Dubbo、Kafka、Spark等分散式叢集上得到廣泛應用。

ZNode

Zookeeper的資料模型為樹狀結構,樹的節點被稱作ZNode。

Zookeeper使用路徑來唯一標識ZNode,類似於Unix檔案系統中的絕對路徑。路徑必須以/開頭,由Unicode字串組成,如/myapp/master/0

每一個ZNode維護著三部分資料:

  • stat: 節點狀態資訊,包括版本、更改時間、訪問控制等
  • data: 節點的內容資料,Zookeeper限制每個節點資料不超過1M。Zookeeper設計用來進行協調排程,請勿在其中存放大量資料。
  • children: 該ZNode的子節點。

每個節點都有獨立的訪問控制列表(Access Control List, ACL), 來控制使用者對本節點的訪問許可權。

每個ZNode都維護著三個版本號:

  • dataVersion: 節點資料版本號
  • cversion: 子節點版本號
  • aclVersion: 節點訪問控制列表版本號

所有的寫操作都會使相應的版本號增加。寫操作必須指定要更新的ZNode的版本號,版本號不一致會導致寫入失敗。

Zxid

所有對Zookeeper狀態的改變都會產生一個Zxid(ZooKeeper Transaction Id),Zxid全域性有序。

Zxid為標識事件發生的先後順序: 即事件A發生早於事件B,那麼事件A的Zxid定小於事件B的Zxid。

每個ZNode維護兩個Zxid:

  • cZxid: Znode建立
  • mZxid: Znode最近修改

Zxid是一個64位的數字, 高32位表示Zookeeper叢集leader, 低32位表示邏輯順序。每次leader改變後, 新產生的Zxid高32位都會改變。

節點型別

Zookeeper中的節點分為兩種:

  • 臨時節點: 臨時節點依賴於建立節點的會話(Session), 一旦Session結束臨時節點會被自動刪除。臨時節點不允許擁有子節點
  • 永久節點: 永久節點生存期不依賴於客戶端會話,只有客戶端執行刪除操作時才會刪除。

Zookeeper 可以建立順序子節點,即建立子節點時在路徑結尾新增一個自增的32位 id, 該id在該節點的父節點下是唯一的。

Watch

Zookeeper 所有的讀操作getData(), getChildren()和 exists()都可以設定 Watch 觸發器。

Watch 觸發器是一次性的,當觸發器通知了一次狀態變化後消失,不會通知狀態的再次變化。

Zookeeper 與客戶端之間通過 Tcp Socket 進行通訊, Zookeeper 會主動將時間通知客戶端。

Zookeeper 保證客戶端只有首先收到了Watch通知後,才會感知到它所設定監視的znode發生了變化。

Zookeeper 支援三種型別的watch:

  • exists: 被監視的Znode 建立、刪除、資料改變時被觸發
  • getData: 被監視的Znode 刪除、資料改變時被觸發
  • getChildren: 被監視的Znode 刪除、建立子節點、刪除子節點時被觸發

Zookeeper 應用

ZooKeeper 是具有較高一致性的分散式協調服務,它提供以下保證:

  • 原子性: 所有操作具有原子性
  • 分散式一致性: 從任意節點中讀取到的資料都是一致的
  • 順序一致性: 從一個客戶端來的更新請求會被順序執行; 寫操作是全域性有序的;
  • 永續性: 寫操作一旦成功,直到被覆蓋為止持續有效

Zookeeper 使用資料副本和崩潰恢復機制保證資料安全和叢集高可用性。

Zookeeper 使用基於 Paxos 演算法的 ZAB協議(Zookeeper Atomic Broadcast)進行寫操作,保證叢集資料的一致性。

配置中心

我們可以將系統中通用配置資訊寫入 ZNode 中,客戶端啟動時從 Zookeeper 獲取配置資料並監視配置節點的變化,當配置發生改變 Zookeeper 會通知所有的客戶端獲取最新資料,從而實現線上更新配置。

適合使用Zookeeper維護的配置通常:

  • 資料量較小
  • 在執行時動態發生變化
  • 各客戶端讀取到資料需要相同
  • 具有順序一致性,客戶端只要讀取到新版本的資料,此後就不能讀取到舊版本資料

當服務啟動時,服務提供者可以在Zookeeper的相應路徑下建立臨時節點,並在節點中寫入服務配置資訊。服務關閉(崩潰)時,臨時節點自動刪除。

客戶端啟動時從 ZooKeeper 讀取服務提供者資訊從而實現自動的服務釋出/移除功能。

分散式鎖

Zookeeper 的臨時節點可以維護客戶端持有鎖的狀態,加鎖失敗的客戶端可以使用 Watch 機制監視鎖的釋放情況,實現阻塞等待加鎖。

Zookeeper 的順序節點可以實現一個簡單的佇列,可以利用此特性實現公平鎖。客戶端在鎖節點下建立順序子節點,持有最小子節點的客戶端成功加鎖,加鎖失敗的客戶端 Watch 前一個順序子節點,從而實現先到先得的公平鎖機制。

命名服務

ZooKeeper 的順序節點可以生成全域性唯一ID, 我們可以利用該ID為服務命名。相對於UUID, 該名稱較短且可以保證絕不重複。

Master選舉

與分散式公平鎖應用類似,ZooKeeper 可以維護叢集 Master。

叢集中所有可以成為 Master 的程式都在 Zookeeper 中的指定路徑下建立順序子節點,持有最小子節點的程式成為Master。

叢集中所有程式都 Watch 指定路徑下節點的情況,一旦發生變化則重新讀取最小子節點的持有者作為Master。

腦裂問題

傳統叢集實現方案是執行一個備用Master節點,備用Master節點定期向主Master節點傳送ping請求,若能及時收到主Master的ack響應則認為正常。

若Ack響應超時,備用Master則會取代原主Master成為新的叢集主Master節點。

ZooKeeper概念與應用

若響應超時因為主Master故障導致,備用Master成為新的主節點完全正常。

若超時因為主備 Master 節點間 ping-ack 網路故障導致,那麼主Master工作正常,而備用Master卻誤認為主Master崩潰而進行取代,那麼叢集中可能出現多個Master共存的故障(即腦裂故障)。

若使用 Zookeeper 維護 Master 資訊,無論是因為主Master故障還是通訊問題導致最小子節點被刪除,備用Master持有的節點都會成為最小子節點。

此時,所有客戶端都會受到通知並得知 Master 變更,保證叢集中只有一個 Master。

當崩潰的Master恢復後,它將成為新的備用Master加入叢集。

ZooKeeper 無法避免通訊故障導致誤判 Master 狀態,但是可以保證在任何情況下叢集中只有一個 Master 節點。

相關文章