Zookeeper一致性協議——ZAB

Jacian發表於2020-12-30

ZAB協議簡介

Zookeeper通過ZAB保證分散式事務的最終一致性。

ZAB全稱Zookeeper Atomic Broadcast(ZAB,Zookeeper原子訊息廣播協議)

  1. ZAB是一種專門為Zookeeper設計的一種支援 崩潰恢復原子廣播協議 ,是Zookeeper保證資料一致性的核心演算法。ZAB借鑑了Paxos演算法,但它不是通用的一致性演算法,是特別為Zookeeper設計的。

  2. 基於ZAB協議,Zookeeper實現了⼀種主備模式的系統架構來保持叢集中各副本之間的資料的⼀致性,表現形式就是使⽤⼀個單⼀的主程式(Leader伺服器)來接收並處理客戶端的所有事務請求(寫請求),並採⽤ZAB的原⼦⼴播協議,將伺服器資料的狀態變更為事務 Proposal的形式⼴播到所有的Follower程式中。

問題提出

  • 主從架構下,leader 崩潰,資料一致性怎麼保證?
  • 選舉 leader 的時候,整個叢集無法處理寫請求的,如何快速進行 leader 選舉?

ZAB過程

ZAB協議的核⼼是 定義了對於那些會改變Zookeeper伺服器資料狀態的事務請求的處理⽅式

所有事務必須由一個 全域性唯一的伺服器來協調處理 ,這樣的伺服器被稱為Leader伺服器,餘下的伺服器則稱為Follower伺服器

  1. Leader伺服器負責將一個客戶端事務請求轉化為一個事務Proposal(提案),並將該Proposal分發給叢集中所有的Follower伺服器
  2. Leader伺服器等待所有Follower伺服器的反饋,一旦超過半數的Follower伺服器進行了正確的反饋後,Leader就會向所有的Follower伺服器傳送Commit訊息,要求將前一個Proposal進行提交。

ZAB協議內容簡介

ZAB協議包括兩種基本的模式: 崩潰恢復訊息廣播

訊息廣播

當叢集中有過半的Follower伺服器完成了和Leader伺服器的狀態同步,那麼整個服務框架就可以進入 訊息廣播模式

當一臺遵守ZAB協議的伺服器啟動後加入到叢集中,如果此時叢集中已經存在一個Leader伺服器在負責進行訊息廣播,那麼加入的伺服器會自覺的進入 資料恢復模式: 找到Leader 所在的伺服器,並與其進⾏資料同步,資料同步完成後參與到訊息⼴播流程中。

ZAB協議的訊息廣播使用原子廣播協議, 類似一個二階段提交的過程 ,但又有所不同。

  1. 二階段提交中,需要所有參與者反饋ACK後再傳送Commit請求。要求所有參與者要麼成功,要麼失敗。這樣會產生嚴重的阻塞問題
  2. ZAB協議中,Leader等待半數以上的Follower成功反饋ACK即可,不需要收到全部的Follower反饋ACK。

訊息廣播過程:

  1. 客戶端發起寫請求
  2. Leader將客戶端請求資訊轉化為事務Proposal,同時為每個Proposal分配一個事務ID(Zxid)
  3. Leader為每個Follower單獨分配一個FIFO的佇列,將需要廣播的Proposal依次放入到佇列中
  4. Follower接收到Proposal後,首先將其以事務日誌的方式寫入到本地磁碟中,寫入成功後給Leader反饋一個ACK響應
  5. Leader接收到半數以上Follower的ACK響應後,即認為訊息傳送成功,可以傳送Commit訊息
  6. Leader向所有Follower廣播Commit訊息,同時自身也會完成事務提交。Follower接收到Commit訊息後也會完成事務的提交

崩潰恢復

在整個服務框架啟動過程中,如果Leader伺服器出現網路中斷、崩潰退出或重啟等異常情況,ZAB協議就會進入崩潰恢復模式。同時選舉出新的Leader伺服器。

當選舉產生了新的Leader伺服器,同時叢集中已經有過半的機器與該Leader伺服器完成了狀態同步(資料同步)之後,ZAB協議會退出恢復模式。

  1. 在ZAB協議中,為了保證程式的正確運⾏,整個恢復過程結束後需要選舉出⼀個新的Leader 伺服器。
  2. Leader選舉演算法不僅僅需要讓Leader⾃身知道已經被選舉為Leader,同時還需要讓叢集中的所有其他機器也能夠快速地感知到選舉產⽣出來的新Leader伺服器。

ZAB保證資料一致性

ZAB協議規定了 如果⼀個事務Proposal在⼀臺機器上被處理成功,那麼應該在所有的機器上都被處理成功,哪怕機器出現故障崩潰。 針對這些情況ZAB協議需要保證以下條件:

  • 已經在Leader伺服器上提交的事務最終被所有伺服器都提交。

    假設⼀個事務在 Leader 伺服器上被提交了,並且已經得到過半 Folower 伺服器的Ack反饋,但是在它 將Commit訊息傳送給所有Follower機器之前,Leader伺服器掛了

  • 丟棄只在Leader伺服器上被提出(未提交)的事務。

    假設初始的 Leader 伺服器 Server1 在提出了⼀個事務Proposal3 之後就崩潰退出 了,從⽽導致叢集中的其他伺服器都沒有收到這個事務Proposal3。於是,當 Server1 恢復過來再次加 ⼊到叢集中的時候,ZAB 協議需要確保丟棄Proposal3這個事務。

綜上所述,ZAB的選舉出來的Leader必須滿足以下條件:

能夠確保提交已經被 Leader 提交的事務 Proposal,同時丟棄已經被跳過的事務 Proposal。即:

  1. 新選舉出來的 Leader 不能包含未提交的 Proposal。
  2. 新選舉的 Leader 節點中含有最大的 zxid

ZAB如何資料同步

所有正常執行的伺服器要麼成為Leader,要麼成為Follower並和Leader保持同步。

  1. 完成Leader選舉(新的 Leader 具有最高的zxid)之後,在正式開始⼯作(接收客戶端請求)之前,Leader伺服器會⾸先確認事務⽇志中的所有Proposal是否都已經被叢集中過半的機器提交了,即 是否完成資料同步

  2. Leader伺服器需要確保所有的Follower伺服器能夠接收到每⼀條事務Proposal,並且能夠正確地將所有已經提交了的事務Proposal應⽤到記憶體資料中。等到 Follower伺服器將所有其尚未同步的事務 Proposal 都從 Leader 伺服器上同步過來併成功應⽤到本地資料庫中後,Leader伺服器就會將該Follower伺服器加⼊到真正的可⽤Follower列表中,並開始之後的其他流程。

ZAB執行時狀態

ZAB協議設計中,每個程式都有可能處於如下三種狀態之一:

  • LOOKING:Leader選舉狀態,正在尋找Leader
  • FOLLOWING:當前節點是Follower。與Leader伺服器保持同步狀態
  • LEADING:當前節點是Leader,作為主程式領導狀態。

ZAB狀態的切換

啟動時的狀態轉換

  1. 所有程式的初始狀態都是LOOKING狀態,此時不存在Leader。

  2. 接下來,程式會試圖選舉出來一個新的Leader,Leader切換為LEADING狀態,其它程式發現已經選舉出新的Leader,那麼它就會切換到FOLLOWING狀態,並開始與Leader保持同步。

  3. 處於FOLLOWING狀態的程式稱為Follower,LEADING狀態的程式稱為Leader。

  4. 當Leader崩潰或者放棄領導地位時,其餘的Follower程式就會切換到LOOKING狀態開始新一輪的Leader選舉。

執行過程中的狀態轉換

一個Follower只能和一個Leader保持同步,Leader程式和所有的Follower程式之間通過心跳監測機制來感知彼此的情況。

  1. 若Leader能夠在超時時間內正常的收到心跳檢測,那麼Follower就會一直與該Leader保持連線。
  2. 如果在指定時間內Leader無法從過半的Follower程式那裡接收到心跳檢測,或者TCP連線斷開,那麼Leader會放棄當前週期的領導,並轉換為LOOKING狀態;其他的Follower也會選擇放棄這個Leader,同時轉換為LOOKING狀態,之後會進行新一輪的Leader選舉

ZAB的四個階段

選舉階段(Leader Election)

節點在一開始都處於選舉階段,只要有一個節點超過半數階段的票數,它就可以當選準Leader,只有到達第三個階段(同步階段),這個準Leader才會成為真正的Leader。

這一階段的目的就是為了選出一個準Leader,然後進入下一階段。

發現階段

在這個階段中,Followers和上一輪選舉出的準Leader進行通訊,同步Followers最近接受的事務Proposal。這個階段主要目的是發現當前大多數節點接受的最新提議,並且準Leader生成新的epoch,讓Followers接受,更新它們的acceptedEpoch。

一個Follower只會連線一個Leader,如果有一個節點F認為另一個Follower P是Leader,F在嘗試連線P時會被拒絕,F被拒絕後,就會進入選舉階段。

ZAB-發現階段

同步階段

同步階段主要是利用 Leader 前一階段獲得的最新 Proposal 歷史,同步叢集中所有的副本

只有當 quorum(超過半數的節點) 都同步完成,準 Leader 才會成為真正的 Leader。Follower 只會接收 zxid 比自己 lastZxid 大的 Proposal。

ZAB同步階段

廣播階段

到了這個階段,Zookeeper 叢集才能正式對外提供事務服務,並且 Leader 可以進行訊息廣播。同時,如果有新的節點加入,還需要對新節點進行同步。
需要注意的是,Zab 提交事務並不像 2PC 一樣需要全部 Follower 都 Ack,只需要得到 quorum(超過半數的節點)的Ack 就可以。

ZAB廣播階段

ZAB協議實現

Java 版本的ZAB協議的實現跟上面的定義略有不同,選舉階段使用的是 Fast Leader Election(FLE),它包含了步驟2的發現職責。因為FLE會選舉擁有最新提議的歷史節點作為 Leader,這樣就省去了發現最新提議的步驟。

實際的實現將 發現和同步階段合併為 Recovery Phase(恢復階段) ,所以,Zab 的實現實際上有三個階段。

快速選舉(Fast Leader Election)

前面提到的 FLE 會選舉擁有最新Proposal history (lastZxid最大)的節點作為 Leader,這樣就省去了發現最新提議的步驟。 這是基於擁有最新提議的節點也擁有最新的提交記錄

成為Leader的條件:

  1. 選epoch最大的
  2. epoch相等,選zxid最大的
  3. epoch和zxid都相等,選server_id最大的(zoo.cfg 中配置的 myid)

節點在選舉開始時,都預設投票給自己,當接收其他節點的選票時,會根據上面的 Leader條件 判斷並且更改自己的選票,然後重新傳送選票給其他節點。當有一個節點的得票超過半數,該節點會設定自己的狀態為 Leading ,其他節點會設定自己的狀態為 Following

恢復階段(Recovery Phase)

這一階段 Follower 傳送他們的 lastZxid 給 Leader,Leader 根據 lastZxid 決定如何同步資料。這裡的實現跟前面的 階段 3 有所不同:Follower 收到 TRUNC 指令會終止 L.lastCommitedZxid 之後的 Proposal ,收到 DIFF 指令會接收新的 Proposal。

history.lastCommittedZxid:最近被提交的提議的 zxid
history.oldThreshold:被認為已經太舊的已提交提議的 zxid

廣播階段(Broadcast Phase)

參考 4.1 [ZAB協議內容#訊息廣播]

ZAB與Paxos的聯絡和區別

聯絡

  1. 都存在一個類似Leader程式的角色,由其負責協調多個Follower程式的執行
  2. Leader程式都會等待超過半數的Follower作出正確的反饋後,才會將一個提議進行提交(過半原則
  3. 在ZAB中,每個Proposal中都包含了一個epoch值,用來代表當前Leader週期,在Paxos中同樣存在這樣的一個表示,名字為 Ballot。

區別

  1. Paxos演算法中,新選舉產生的主程式會進行兩個階段的工作;第一階段稱為讀階段:新的主程式和其他程式通訊來收集主程式提出的提議,並將它們提交。第二階段稱為寫階段:當前主程式開始提出自己的提議。
  2. ZAB協議在Paxos基礎上新增了同步階段,此時,新的Leader會確儲存在過半的Follower已經提交了之前Leader週期中的所有事物Proposal。這一同步階段的引入,能夠有效保證,Leader在新的週期中提出事務Proposal之前,所有的程式都已經完成了對之前所有事務Proposal的提交。

總的來說,ZAB協議和Paxos演算法的本質區別在於兩者的設計目的不一樣:ZAB協議主要用於構建一個高可用的分散式資料主備系統,而Paxos演算法則用於構建一個分散式的一致性狀態機系統。

總結

問題解答:

  • 主從架構下,leader 崩潰,資料一致性怎麼保證?

    leader 崩潰之後,叢集會選出新的 leader,然後就會進入恢復階段,新的 leader 具有所有已經提交的提議,因此它會保證讓 followers 同步已提交的提議,丟棄未提交的提議(以 leader 的記錄為準),這就保證了整個叢集的資料一致性。

  • 選舉 leader 的時候,整個叢集無法處理寫請求的,如何快速進行 leader 選舉?

    這是通過 Fast Leader Election 實現的,leader 的選舉只需要超過半數的節點投票即可,這樣不需要等待所有節點的選票,能夠儘早選出 leader。

相關文章