ZAB 協議是爲分佈式協調服務 ZooKeeper 專門設計的一種支持崩潰恢復的原子廣播協議。在 ZooKeeper 中,主要依賴 ZAB 協議來實現分佈式數據一致性,基於該協議,ZooKeeper 實現了一種主備模式的系統架構來保持集羣中各個副本之間的數據一致性。算法
ZAB 是 Zookeeper 原子廣播協議的簡稱,下面咱們來討論協議的內容,注意:理論與實現是有區別的,若是你對協議的理論不感興趣,能夠直接跳過看實現。服務器
Zookeeper 客戶端會隨機鏈接到 Zookeeper 集羣的一個節點,若是是讀請求,就直接從當前節點中讀取數據;若是是寫請求,那麼節點就會向 leader 提交事務,leader 會廣播事務,只要有超過半數節點寫入成功,該寫請求就會被提交(類 2PC 協議)。架構
那麼問題來了:分佈式
帶着這兩個問題,咱們來看看 ZAB 協議是如何解決的。atom
ZAB 中的節點有三種狀態設計
代碼實現中多了一種:observing 狀態,這是 Zookeeper 引入 Observer 以後加入的,Observer 不參與選舉,是隻讀節點,跟 ZAB 協議沒有關係日誌
節點的持久狀態code
在 ZAB 協議的事務編號 Zxid 設計中,Zxid 是一個 64 位的數字,其中低 32 位是一個簡單的單調遞增的計數器,針對客戶端每個事務請求,計數器加 1;而高 32 位則表明 Leader 週期 epoch 的編號,每一個當選產生一個新的 Leader 服務器,就會從這個 Leader 服務器上取出其本地日誌中最大事務的ZXID,並從中讀取 epoch 值,而後加 1,以此做爲新的 epoch,並將低 32 位從 0 開始計數。server
epoch:能夠理解爲當前集羣所處的年代或者週期,每一個 leader 就像皇帝,都有本身的年號,因此每次改朝換代,leader 變動以後,都會在前一個年代的基礎上加 1。這樣就算舊的 leader 崩潰恢復以後,也沒有人聽他的了,由於 follower 只遵從當前年代的 leader 的命令。*blog
節點在一開始都處於選舉階段,只要有一個節點獲得超半數節點的票數,它就能夠當選準 leader。只有到達 Phase 3 準 leader 纔會成爲真正的 leader。這一階段的目的是就是爲了選出一個準 leader,而後進入下一個階段。
協議並無規定詳細的選舉算法,後面咱們會提到實現中使用的 Fast Leader Election。
在這個階段,followers 跟準 leader 進行通訊,同步 followers 最近接收的事務提議。這個一階段的主要目的是發現當前大多數節點接收的最新提議,而且準 leader 生成新的 epoch,讓 followers 接受,更新它們的 acceptedEpoch
一個 follower 只會鏈接一個 leader,若是有一個節點 f 認爲另外一個 follower p 是 leader,f 在嘗試鏈接 p 時會被拒絕,f 被拒絕以後,就會進入 Phase 0。
同步階段主要是利用 leader 前一階段得到的最新提議歷史,同步集羣中全部的副本。只有當 quorum 都同步完成,準 leader 纔會成爲真正的 leader。follower 只會接收 zxid 比本身的 lastZxid 大的提議。
到了這個階段,Zookeeper 集羣才能正式對外提供事務服務,而且 leader 能夠進行消息廣播。同時若是有新的節點加入,還須要對新節點進行同步。
值得注意的是,ZAB 提交事務並不像 2PC 同樣須要所有 follower 都 ACK,只須要獲得 quorum (超過半數的節點)的 ACK 就能夠了。
協議的 Java 版本實現跟上面的定義有些不一樣,選舉階段使用的是 Fast Leader Election(FLE),它包含了 Phase 1 的發現職責。由於 FLE 會選舉擁有最新提議歷史的節點做爲 leader,這樣就省去了發現最新提議的步驟。實際的實現將 Phase 1 和 Phase 2 合併爲 Recovery Phase(恢復階段)。因此,ZAB 的實現只有三個階段:
前面提到 FLE 會選舉擁有最新提議歷史(lastZixd最大)的節點做爲 leader,這樣就省去了發現最新提議的步驟。這是基於擁有最新提議的節點也有最新提交記錄的前提。
epoch
最大的epoch
相等,選 zxid 最大的epoch
和zxid
都相等,選擇server id
最大的(就是咱們配置zoo.cfg
中的myid
)節點在選舉開始都默認投票給本身,當接收其餘節點的選票時,會根據上面的條件更改本身的選票並從新發送選票給其餘節點,當有一個節點的得票超過半數,該節點會設置本身的狀態爲 leading,其餘節點會設置本身的狀態爲 following。
這一階段 follower 發送它們的 lastZixd 給 leader,leader 根據 lastZixd 決定如何同步數據。這裏的實現跟前面 Phase 2 有所不一樣:Follower 收到 TRUNC 指令會停止 L.lastCommittedZxid 以後的提議,收到 DIFF 指令會接收新的提議。
history.lastCommittedZxid:最近被提交的提議的 zxid
history:oldThreshold:被認爲已經太舊的已提交提議的 zxid
通過上面的分析,咱們能夠來回答開始提到的兩個問題
這篇文章是根據我對 ZAB 協議的理解寫成的,若是以爲有些細節沒有講清楚,能夠看後面的參考資料,我主要是參考這篇論文的。
參考資料
ZooKeeper’s atomic broadcast protocol:Theory and practice
原文:http://blog.jobbole.com/104985/