ZAB 協議,全稱 Zookeeper Atomic Broadcast(Zookeeper 原子廣播協議),是為分布式協調服務 ZooKeeper 專門設計的一種支持崩潰恢復的一致性協議。基于該協議,ZooKeeper 實現了一種主從模式的系統架構來保持集群中各個副本之間的數據一致性。當 Zookeeper 集群中的一臺服務器出現以下兩種情況之一時,需要進入 Leader 選舉:(1)服務器初始化啟動;(2)服務器運行期間 Leader 故障。
~
本篇內容包括:關于 ZAB 協議、Zookeeper 選主時機、Zookeeper 選主機制。
文章目錄
- 一、關于 ZAB 協議
- 1、ZAB 協議簡述
- 2、ZooKeeper 集群中的三個服務器角色
- 二、Zookeeper 選主時機
- 1、ZooKeeper 服務器的工作狀態
- 2、Zookeeper 選主時機
- 3、FOLLOWING 狀態節點(Follower)的主流程
- 4、LOOKING 狀態節點的主流程
- 5、LEADING 狀態節點(Leader)的主流程
- 三、Zookeeper 選主機制
- 1、涉及到的相關概念
- 2、Zookeeper 選舉流程
- 3、集團初始選舉
- 4、集群重新選舉
- 5、選舉流程總結
一、關于 ZAB 協議
1、ZAB 協議簡述
ZAB 協議,全稱 Zookeeper Atomic Broadcast(Zookeeper 原子廣播協議),是為分布式協調服務 ZooKeeper 專門設計的一種支持崩潰恢復的一致性協議。基于該協議,ZooKeeper 實現了一種主從模式的系統架構來保持集群中各個副本之間的數據一致性。
作為分布式共識算法的一員,Zab 算法構成了著名的 ZooKeeper 的基石。與赫赫有名的 Paxos、Raft 一樣,Zab 算法也提供了強一致性的保證。
從設計上看,ZAB 協議和 Raft 很類似。ZooKeeper 集群中,只有一個 Leader 節點,其余均為 Follower 節點。
2、ZooKeeper 集群中的三個服務器角色
Zookeeper 集群中的機器分為以下三種角色:
- Leader:①、整個 Zookeeper 集群工作機制中的核心,過選舉產生的集群領導者,提供讀寫服務;②、一個 Zookeeper 集群中同一時間只能有一個實際工作的 Leader,它用來維護各個 Follow 與 Observer 之間的心跳;③、Leader 是事務請求的唯一調度和處理者,Follow 接收到事務請求會將請求轉發給 Leader 處理。
- Follow:①、Follow 只提供讀服務,即只處理非事務請求,它接收到事務請求會轉發給 Leader 服務器;②、它參與 Leader 的選舉,參與事務請求 Proposal 的投票;③、一個 Zookeeper 集群同時可以有多個 Follow。
- Observer:①、功能和 Follow 基本一致,提供讀服務,即只處理非事務請求,唯一的差別是不參與任何投票(包括事務請求 Proposal 和 Leader 的選舉);②、Observer 的作用主要就是在不影響集群事務處理前提下提升集群的非事務處理。
二、Zookeeper 選主時機
1、ZooKeeper 服務器的工作狀態
ZooKeeper 服務器有四種工作狀態:
-
LOOKING:競選狀態,尋找 Leader。當服務器處于該狀態時,它會認為當前服務器沒有 Leader,因此需要進入 Leader 選舉狀態。
-
FOLLOWING:跟隨者狀態。表明當前服務器角色是 Follower。
-
LEADING:領導者狀態。表明當前服務器角色是 Leader。
-
OBSERVING:觀察者狀態。表明當前服務器角色是 Observer。
2、Zookeeper 選主時機
當 Zookeeper 集群中的一臺服務器出現以下兩種情況之一時,需要進入 Leader 選舉:(1)服務器初始化啟動;(2)服務器運行期間 Leader 故障。
- 服務器初始化啟動:每個節點啟動的時候狀態都是 LOOKING,處于觀望狀態,接下來就是要進行選主了。
- 服務器運行期間 Leader 故障:Leader 節點運行后會周期性地向 Follower 發送心跳信息(稱之為 ping)
- 如果一個 Follower 未收到 Leader 節點的心跳信息,Follower 節點的狀態會從 FOLLOWING 轉變為 LOOKING;
- Leader 節點也會檢測 Follower 節點的狀態,如果多數 Follower 節點不再響應 Leader 節點(可能是 Leader 節點與 Follower 節點之間產生了網絡分區),那么 Leader 節點可能此時也不再是合法的 Leader 了,也必須要進行一次新的選主。
3、FOLLOWING 狀態節點(Follower)的主流程
FOLLOWING 狀態節點(Follower)的主流程:
void followLeader() throws InterruptedException {
try {......while (this.isRunning()) {readPacket(qp);processPacket(qp);}// 如果上面的 while 循環內出現異常// Ps:長時間沒有收到 Leader 的消息也是異常
} catch (Exception e) {// 出現異常就退出了 while 循環// 也就結束了 Follower 的處理流程
}
4、LOOKING 狀態節點的主流程
LOOKING 狀態節點的主流程:
public void run() {while (running) {switch (getPeerState()) {case FOLLOWING:try {setFollower(makeFollower(logFactory));follower.followLeader();} catch (Exception e) {......} finally {follower.shutdown();setFollower(null);// 狀態更新為 LOOKINGupdateServerState();}break;......}
}
5、LEADING 狀態節點(Leader)的主流程
在 Leader 節點的主循環流程中,會判斷多數派節點的消息狀態,如下:
void lead() throws IOException, InterruptedException {......while (true) {......// 判斷每個每個 Follower 節點的狀態// 是否與 Leader 保持同步for (LearnerHandler f : getLearners()) {if (f.synced()) { syncedAckSet.addAck(f.getSid());}}......}if (!tickSkip && !syncedAckSet.hasAllQuorums()) {// 如果失去了大多數 Follower 節點的認可,就跳出 Leader 主循環,進入選主流程break;}......
}// LearnerHandler::synced() 邏輯
// 即判斷當前是否已經過了期望得到的 Follower 的下一個消息的期限:tickOfNextAckDeadline
public boolean synced() {return isAlive() && leader.self.tick.get() <= tickOfNextAckDeadline;
}
三、Zookeeper 選主機制
1、涉及到的相關概念
# Server id(myid 或 sid):服務器 ID
比如有三臺服務器,編號分別是 1,2,3。編號越大在選擇算法中的權重越大,比如初始化啟動時就是根據服務器 ID 進行比較。
# Zxid:事務ID
服務器中存放的數據的事務 ID,值越大說明數據越新,在選舉算法中數據越新權重越大。
zxid 有兩部分組成:高 32位 是 epoch,低 32位 是 epoch 內的自增 id,由 0 開始。每次選出新的 Leader,epoch 會遞增,同時 zxid 的低 32 位清 0
# Epoch:邏輯時鐘
也叫投票的次數,同一輪投票過程中的邏輯時鐘值是相同的,每投完一次票這個數據就會增加。
2、Zookeeper 選舉流程
選舉大致流程:
- 初始投票:服務器啟動后,每個 Server 都會給自己投上一票,每次投票會包含所投票服務器的 myid 和 zxid
- 同步投票結果:集群中的服務器在投票后,會將各自的投票結果同步給集群中其他服務器。
- 檢查投票有效性:各服務器在收到投票后會檢查投票的有效性,如:是否本輪投票,是否來自 LOOKING 狀態的服務器的投票等。
- 處理投票:服務器之間會進行投票比對,規則如下:①、優先檢查 zxid,較大的服務器優先作為 Leader;②、如果 zxid 相同,則 myid 較大的服務器作為 Leader;
- 統計投票結果:每輪投票比對之后都會統計投票結果,確認是否有超過半數的機器都得到相同的投票結果,如果是,則選出 Leader,否則繼續投票。
- 更改服務器狀態:一旦選出 Leader,每個服務器就會各自更新自己的狀態
3、集團初始選舉
假設我們有服務器 1~5,服務器初始化啟動選主流程(粗略版):
- 「服務器1」啟動,發起一次選舉。「服務器1」投自己一票。此時「服務器1」票數一票,不夠半數以上(3票),選舉無法完成,「服務器1」狀態保持為 LOOKING;
- 「服務器2」啟動,再發起一次選舉。「服務器1」和「服務器2」分別投自己一票并交換選票信息;此時「服務器1」發現「服務器2」的 myid 比自己目前投票推舉的「服務器1」大,更改選票為推舉「服務器2」。此時「服務器1」票數 0 票,服「務器2」票數 2 票,沒有半數以上結果,選舉無法完成,「服務器1」,「服務器2」保持 LOOKING;
- 「服務器3」啟動,發起一次選舉。此時「服務器1」和「服務器2」都會更改選票為「服務器3」。此次投票結果:「服務器1」、「服務器2」為 0。票,「服務器3」為 3。票。此時「服務器3」的票數已經超過半數,「服務器3」當選成為 Leader。「服務器1」、「服務器2」更改狀態為 FOLLOWING,「服務器3」更改狀態為 LEADING;
- 「服務器4」啟動,發起一次選舉。此時「服務器1,2,3」已經不是 LOOKING 狀態,不會更改選票信息。交換選票信息結果:「服務器3」為 3 票,「服務器4」為 1 票。此時「服務器4」服從多數,更改選票信息為「服務器3」,并更改狀態為 FOLLOWING;
- 「服務器5」同「服務器4」。
4、集群重新選舉
Zookeeper 集群運行期間無法和 Leader 保持正常連接時,即如果 Leader 掛了,或者 Leader 服務器故障都會進行新一輪的 Leader 選舉。需要重新選舉時,選舉過程就需要加入數據 id,服務器id,和邏輯時鐘。
集群重新選舉時,根據 myid 和 zxid 的大小共同決斷,zxid 更大的優先成為 Leader,選舉的標準(粗略版):
- 邏輯時鐘小的選舉結果會被忽略,重新投票;
- 統一邏輯時鐘后,事務 id 大的勝出,當選 Leader;
- 事務 id 相同的情況下,服務器 id 大的勝出,當選 Leader。
5、選舉流程總結
總結:Zookeeper 集群按 myid 從小到大依次啟動初始化時,在超過半數機器的投票的情況下,誰的 myid 最后,誰就是 Leader,知道這個定律,不同的集群規模我們都可以推算出誰是 Leader。
# 集群初始化時:
- 集群有 3 臺機器,第 2 大的 myid 所在服務器就是 Leader
- 集群有 4 臺機器,第 3 大的 myid 所在服務器就是 Leader;
- 集群有 5 臺機器,第 3 大的 myid 所在服務器就是 Leader;
- 集群有 6 臺機器,第 4 大的 myid 所在服務器就是 Leader;
集群重新選舉時,根據 myid 和 zxid 的大小共同決斷,zxid 更大的優先成為 Leader。