ZAB協議簡介
Zookeeper 使用 Zookeeper Atomic Broadcast (ZAB) 協議來保障分散式資料一致性。
ZAB是一種支援崩潰恢復的訊息廣播協議,採用類似2PC的廣播模式保證正常執行時效能,並使用基於 Paxos 的策略保證崩潰恢復時的一致性。
在閱讀本文前建議先了解2PC和Paxos
ZAB協議中節點存在四種狀態:
- Leading: 當前節點為叢集 Leader,負責協調事務
- Following: 當前節點為 Follower 在 Leader 協調下執行事務
- Looking: 叢集沒有正在執行的 Leader, 正處於選舉過程
- Observing: 節點跟隨 Leader 儲存系統最新的狀態提供讀服務,但不參與選舉和事務投票
因為 Observing 節點不參與事務和選舉,因此下文所述節點不包括 Observing 節點
ZAB協議存在兩種工作模式:
- 廣播模式: 當叢集正常執行過程中,Leader 使用廣播模式保證各 Follower 節點的一致性
- 恢復模式: 叢集啟動或 Leader 崩潰時系統進入恢復模式,選舉 Leader 並將叢集中各節點的資料同步到最新狀態
Zookeeper 叢集中每個節點都會儲存系統資料的完整副本,可以獨立處理讀請求。
當 Follower 收到寫請求時會將其轉發給 Leader, Leader 為每個寫請求分配唯一的全域性有序的事務ID(Zookeeper Transaction Id, ZXID)。
Leader 在廣播模式下協調各 Follower 完成事務,並保證叢集更新到一致的狀態。
一致性保證
ZAB協議保證叢集的順序一致性而不保證強一致性。
即 Leader 依次完成兩個事務 A、B 時,不能保證所有 Follower 立即更新到最新狀態(不保證強一致性); 只保證所有 Follower 一定時間內會同步到最新狀態(保證最終一致性),且任意 Follower 都認為事務A先於事務B完成,不會亂序(保證全域性順序一致性)。
此外,ZAB協議保證來自同一個 Follower 的兩個事務 A、B 按照 Follower 發出請求的順序(而非 Leader 收到請求的順序)依次執行,不會出現亂序。即保證任意客戶端寫請求的一致性。
ZXID
ZXID 是 Zookeeper 叢集中事務的唯一標識,保證全域性有序。
ZXID 是一個 64 位整數, 高32位為週期號(epoch), 每個 Leader 被選舉後都會增加 epoch 與上任 Leader 區分。低32位是 Leader 開始事務時分配的遞增編號。
ZXID 中的 epoch 可以保證 Leader 崩潰重新選舉後被丟棄的事務不會繼續執行。
廣播模式
廣播模式是一個移除了中斷邏輯的2PC協議:
- Leader 收到寫請求後為其分配一個 ZXID 並生成提案發送給所有 Follower
- Follower 收到提案後寫事務日誌但不提交,成功後返回 ACK 告知 Leader 可以進行提交。
- Leader 收到過半 Follower 的 ACK 響應後發出 commit 請求執行提交
- Leader 收到過半 Follower 對 commit 請求的 ACK 響應後便認為事務已完成。剩餘的 Follower 則會放棄執行此次事務,進入資料同步階段,與叢集達成一致。
ZAB廣播模式相對於完整的2PC移除了中斷邏輯, 且只要過半 Follower 完成即可不需要等待全部 Follower。
崩潰或網路超時的 Follower 可以直接拋棄 Leader,並在資料同步階段與叢集達成一致,這種做法提高了叢集的效能。
因為無法保證所有 Follower 都完成了提交,所以 Zookeeper 無法保證強一致性。
Leader 為每個 Follower 的寫請求維護了一個 FIFO 佇列以保證順序一致性,具體實現方式是根據 TCP 報文的序列號確定請求的先後順序。
恢復模式
當叢集啟動或者Leader崩潰時,Zookeeper 叢集會進入恢復模式選舉新的 Leader 並將叢集同步至最新狀態。
Leader 與過半的 Follower 無法正常通訊即視為崩潰
在崩潰恢復過程中需要保證:
- 已執行的事務不能丟失(Never forget delivered messages)
- 未執行的事務不能繼續執行(Let go of messages that are skipped)
若 Leader 在 commit 階段崩潰,根據已完成的事務不能丟失的原則,這些事務應該繼續完成。
因為叢集中 ZXID 最大的提案是 Leader 崩潰前發出的最新的提案,所以應選擇擁有 ZXID 最大的提案的節點做為新的 Leader。
新 Leader 會將自身日誌中所有未提交事務重新生成提案並協調叢集將其完成, 保證所有被髮送的訊息(delivered messages)都被處理。
若 Leader 在 proposal 階段崩潰,根據未執行的事務不能繼續的原則,節點應當丟棄這些事務。
當新 Leader 被選舉之後會增加 ZXID 的 epoch 值,因此 epoch 值較小的提案可以直接丟棄。
恢復模式分為兩個階段:選舉階段和恢復階段。
上文已經說明恢復階段的任務是 Leader 將未提交事務重新生成提案並協調叢集將其完成,不再贅述。
選舉過程
選舉要保證:
- 叢集中有且只有一個節點作為 Leader, 該 Leader 可以與叢集中過半節點通訊
- 新 Leader 擁有 ZXID 最大的提案
在3.4.0後的Zookeeper的版本只保留了TCP版本的FastLeaderElection選舉演算法
每張選票包含3條資訊:
- vote_sid: 推舉的伺服器ID
- vote_zxid: 推舉的伺服器的最大ZXID
- epoch: 投票的輪數
發起選舉的節點會向所有可通訊節點發送第一張選票,推舉自己作為 Leader。
收到選票的伺服器根據下列規則決定自己的投票:
- 若 epoch 大於自身 epoch 說明上一輪投票已結束,更新自身 epoch 值加入新一輪投票,並清除已結束輪次的資料。
- 選擇自身已知擁有最大ZXID 的伺服器作為 Leader。即伺服器本地儲存(vote_sid, vote_zxid)並初始化為自身(sid, zxid), 若收到的選票中 vote_zxid 更大就更新本地資料,並根據最新資料投出選票。
- 若存在 zxid 相同則選擇 sid 最大的伺服器(作者認為選擇sid最小的也可以)。
若在某輪投票中某個節點收到過半數的相同選票,那麼認為該伺服器為新的 Leader 投票結束。
因為選舉階段要求伺服器收到過半選票才能成為新 Leader, 因此不可能出現叢集中存在兩個 Leader 的現象。
選舉過程是比較典型的 Paxos 演算法過程,選舉過程中不會產生新的 ZXID, 因此不會出現 Paxos 演算法中活鎖的現象。
關於ZAB協議的詳細內容可以閱讀官方論文ofollow,noindex" target="_blank">ZooKeeper’s atomic broadcast protocol: Theory and practice 。