深入理解Zookeeper系列(1):ZAB協議

1. 關於Zookeper

  ZooKeeper是一個集中服務,用於維護配置信息,命名,提供分佈式同步和提供組服務。 所有這些類型的服務都以某種形式被分佈式應用程序使用。 每次實施服務時,都會有很多工作來解決不可避免的錯誤和競爭條件。 由於實施這些服務的困難,最初的應用程序通常會不穩定,這使得它們在變化和難以管理的情況下變得脆弱。 即使正確完成,這些服務的不同實現會導致部署應用程序時的管理複雜性。而Apache ZooKeeper致力於開發和維護一個開源服務器,從而實現高度可靠的分佈式協調。

ZooKeeper允許分佈式進程通過與標準文件系統組織類似的共享分層名稱空間相互協調。名稱空間由znodes組成,它們與文件和目錄類似---ZooKeeper Wiki

Zookeeper的架構圖如下:


image.png

2. Zab協議

ZooKeeper支持客戶端讀取和更新具有高可用性的鍵值對。通過將數據複製到多個節點並讓客戶端從任何節點讀取來實現高可用性。

要理解Zookeeper,首先得熟悉它的協議,Zookeeper的協議就是Zookeeper Atomic Broadcast (ZAB).Zab協議由4個階段組成,先來看下總體的層次.

  • Phase 0 – Leader選舉(選出Leader)
  • Phase 1 – 發現(發現全局最新的事務)
  • Phase 2 – 同步(各個Follower同步最新的事務)
  • Phase 3 – 廣播(正常工作的流程)
image.png

術語定義

  • history:已經被接收的提案的日誌信息(已經完成的事務的記錄)
  • acceptedEpoch:接受到的最後一個NEWEPOCH消息的epoch。此時此刻所在的Epoch(準Leader生成後就生成的Epoch)。
  • currentEpoch:接受到的最後一個NEWLEADER消息的epoch(注意是Leader發過來的)(生成新的Leader之前所在的舊Leader的Epoch)
  • lastZxid:history中最後一個提案的ZXID編號
  • ZXID通常由64位組成。高32位表示Epoch值,翻譯過來指朝代。就是每次換了Leader之後,Epoch值都會增加1,用來表示所在的週期,方便Follower進程區別現在的Leader。由於有些Leader由於網絡原因會與Follower斷開聯繫,當它連上後,現在已經有了Leader,那麼它就不能是Leader.Follower會根據Epoch值來判斷當前的Leader。而後32位用來表示Zxid值。每當處理完一個事務,Epoch值都會增加1。而當重新選了一個Leader,那麼Zxid重新開始計數。

3. ZAB協議實現的前提條件

  • 完整性(Integrity )
    進程Pj如果收到來自進程Pi的消息m,那麼進程Pi—定發送了消息m。
  • 前置性(Prefix)
    如果進程Pj收到了消息m,那麼存在這樣的消息m':如果消息m'是消息m的前置消息,那麼Pj務必先接收到消息m',然後再接收到消息m。我們將存在這種前置性關係的兩個消息表示爲m'm。前置性是整個協議設計中最關鍵的一點,由於每一個消息都有可能是基於之前的消息來進行的,因此所有的消息都必須按照嚴格的先後順序來進行處理。

4. 各個階段詳解

詳細的算法步驟可以參考http://www.tcs.hut.fi/Studies/T-79.5001/reports/2012-deSouzaMedeiros.pdf

  • 階段0: Leader Election(Leader選舉階段)

下篇文章將會講到FLE算法.

        ZooKeeper原來採用了三種選舉算法,後來,淘汰了兩種,只剩下FLE這種算法。使用FLE算法的目的,就是要選出具有最大提交歷史的節點作爲候選Leader,這樣後續的日誌就只需要考慮候選Leader到其Fellower節點的單向同步就可以保證一致性了。在FLE算法中通過篩選具有最大LastZxid(history中最後一個提案的ZXID編號)的節點作爲候選Leader,因爲具有最大LastZxid的節點肯定具有最全的提交歷史。
      在FLE算法中,每個節點都只能投一張選票,只有這樣才能確定過半選票的統計值,其思路就是在投票的過程中,節點之間互相交換信息,然後節點根據自己獲得的信息(發現更好的候選者)不斷地更新自己手中的選票,更新的標準就是具有更新的提案號:要麼具有更新的epoch,或者在相同epoch下具有更大的機器編號。那麼這個迭代更新的過程什麼時候結束呢?
     首先,每一輪的選取會有一個遞增的round number作爲標識,這個值越高優先級越高;其次,每一個節點都有一個狀態標識自己:election和leading/fellowing,同時每個節點都知道集羣中其他節點的個數,以及和他們通信的方式。選舉剛剛開始的時候,每個節點在沒有先驗信息的情況下都把選票投向自己,並把這個消息發送給所有的節點,然後等待其他節點們的響應,節點再收到這個消息的時候:
  (1). 如果選票的round number比較舊,則忽略;
  (2). 如果選票的round number比自己新,則更新自己的round number,並清空上一輪相關的陳舊信息,開始廣播自己新的選票;
  (3). 如果是在同一輪投票中:如果接收到的選票的角色是election,並且該消息附帶更新的提案號,則更新自己的選票並繼續廣播自己的選票;如果收到的選票角色是election,但是消息的提案號比自己舊或者跟自己一樣,則記錄這張選票,而檢查發現自己得到針對某個節點超過集羣半數的選票,自己切換爲leading/fellowing狀態,並轉入Phase Recovery;
         (4). 任何時候一旦收到leading/fellowing的選票,都指明當前集羣中已有有效的候選Leader了,直接更新自己切換入Phase Recovery階段;

  • 階段1: Discovery(發現最後的事務)

  在這個階段,這個階段選出的準Leader(prospective leader)肯定是集羣中過半數機器的投票選出的leader。此時所有節點會把自己的F:acceptedEpoch通過FOLLOWERINFO發送給自己的prospective leader,當那個候選Leader得到過半的FOLLOWERINFO消息時候,會在收到消息中取出所見最大的epoch並將其遞增,這樣之前的Leader就不能再提交新的提案了,然後該候選Leader再將這個新epoch通過NEWEPOCH消息發回給這些節點並等待確認。

  在Fellower節點收到候選Leader發送NEWEPOCH後,將其與自己本地的acceptedEpoch對比,如果比他們大就更新自己acceptedEpoch,並返回ACKEPOCH消息後進入Phase 2,否則切換會Phase 0狀態。候選Leader也只能在收到過半數目的ACKEPOCH纔會進入Phase 2。需要注意的是這裏Fellower發送的ACKEPOCH包含了額外的重要信息——自己最新提交日誌,這樣候選Leader在收集ACKEPOCH的同時就知道哪個Fellower具有最新提交了,選定到這個具有最新提交的Fellower後向其同步日誌。算法和第一階段消息交流示意圖如圖3.1。

①:各個Follower將 FOLLOWERINFO發送給自己的prospective leader(讓Leader來發現最新的事務)
②:候選Leader選出最新的Epoch並將這個新epoch通過NEWEPOCH消息發回給這些節點並等待確認。(把最新的Epoch返回給Follower)
③: Fellower節點收到候選Leader發送NEWEPOCH後,將其與自己本地的acceptedEpoch對比,如果比他們大就更新自己acceptedEpoch,並返回ACKEPOCH消息(把自己的事務上傳)。


未命名文件.png

階段1算法.png
  • 階段2: Synchronization(同步階段)

  進入這個階段後,候選Leader已經確立了最新任期號和最新提交日誌,然後他會把自己的history通過新epoch作爲NEWLEADER消息發送給所有的集羣成員,集羣成員更新自己currentEpoch 並按需同步history信息。完成這個步驟後候選Fellower向Leader發送ACKNEWLEADER消息,而候選Leader得到過半數目的ACKNEWLEADER消息後,會向所有的Fellower發送COMMIT並進入Phase 3,而Fellower接收到COMMIT命令而完成提交後,也會切換到Phase 3。第二階段消息交流信息圖示意圖如下:

①:準Leader發送NewLeader(e’,history) 給follower,方便follower同步最新的事務,從而與Leader一致,達到一致。
②:Follower在收到準Leader的NewLeader(e’,history),它會對它本地的事務,如果需要更新的本地的事務,則更新。更新完之後,發送一個確認消息給準Leader。表明它已經收到。
③準Leader在收到確認信息後,再發送一個commit消息給Follower,通知他們可以提交事務了。Follower在收到之後,完成提交就可以。


image.png

階段二算法
  • 階段3: Broadcast (完成同步後,成員各自完成自己角色的任務)

  到達這個階段後,所有節點檢查自己的prospective leader,如果發現它是自己,就切換到正式Leader的狀態,不是這種情況的節點切換到正式Fellower的狀態,而一致性協議保證此時只可能會有一個Leader。這是整個集羣穩定工作狀態,穩定之後的基本流程也類似於上面提到的Propose-ACK-COMMIT的僞2PC(與標準的2PC有區別)操作。其中2PC是指參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是中止操作。消息傳播示意圖如3.3。

①:客戶端發動事務請求給服務端Leader
②: Leader發送請求執行給每個Follower,讓Follower執行事務。並把do,undo寫入日誌。
③:Follower執行事務,並把事務歷史寫入到日誌中去,方便以後撤銷。並返回事務是否執行成功給Leader。
④:Leader若收到超過半數Follower消息,則發送commit消息,讓所有的Follower提交事務。否則,發送消息讓所有的Follwer回滾事務


image.png

階段3算法

NOTE: 在這個過程中,如果有新的Follower加入,則Leader把最新的Epoch,以及最新的歷史提交記錄發送給Follower,方便同步.

5. 總結

從上面的協議過程看來,整個ZooKeeper只要保持有一半以上的機器保持正常運行,那麼整個集羣的功能還是正常的,不會崩潰.而這得益於一僞2pc的過程,只要一半以上的投票,就可以做出決定.

參考文章:
You Cannot Have Exactly-Once Delivery
ZooKeeper’s Atomic Broadcast Protocol: Theory and Practice
Architecture of ZAB – ZooKeeper Atomic Broadcast protocol
ZooKeeper’s atomic broadcast protocol:Theory and practice

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章