ZooKeeper中實現分佈式數據一致性的ZAB協議詳解

一、ZAB 協議簡介

在深入瞭解ZooKeeper之前,認爲ZooKeeper就是Paxos算法的一個實現。但事實上,ZooKeeper並沒有完全採用Paxos算法,而是使用了一種稱爲ZooKeeper Atomic Broadcast (ZAB, ZooKeeper 原子消息廣播協議)的協議作爲其數據一致性的核心算法。

ZAB協議是爲分佈式協調服務ZooKeeper專門設計的一種支持崩潰恢復的原子廣播協議。
ZAB協議並不像Paxos算法那樣,是一種通用的分佈式一致性算法,它是一種特別爲ZooKeeper設計的崩潰可恢復的原子消息廣播算法。

在ZooKeeper中,主要依賴ZAB協議來實現分佈式數據一致性,基於該協議,ZooKeeper實現了一種主備模式的系統架構來保持集羣中各副本之間數據的一致性。具體的,ZooKeeper使用一個單一的主進程來接收並處理客戶端的所有事務請求,並採用ZAB的原子廣播協議,將服務器數據的狀態變更以事務Proposal的形式廣播到所有的副本進程上去。ZAB協議的這個主備模型架構保證了同一時刻集羣中只能夠有一個主進程來廣播服務器的狀態變更,因此能夠很好地處理客戶端大量的併發請求。另一方面,考慮到在分佈式環境中,順序執行的一些狀態變更其前後會存在一定的依賴關係,有些狀態變更必須依賴於比它早生成的那些狀態變更,例如變更C需要依賴變更A和變更B。這樣的依賴關係也對ZAB協議提出了一個要求:ZAB協議必須能夠保證一個全局的變更序列被順序應用,也就是說,ZAB協議需要保證如果一個狀態變更已經被處理了,那麼所有其依賴的狀態變更都應該已經被提前處理掉了。(意思就是已經處理的事務請求不會再處理,不能丟失)最後,考慮到主進程在任何時候都有可能出現崩潰退出或重啓現象,因此,ZAB協議還需要做到在當前主進程出現上述異常情況的時候,依舊能夠正常工作。

ZAB協議的核心是定義了對於那些會改變ZooKeeper服務器數據狀態的事務請求的處理方式,即:

所有事務請求必須由一個全局唯一的服務器來協調處理,這樣的服務器被稱爲Leader 服務器,而餘下的其他服務器則成爲Follower服務器。Leader服務器負責將一個客戶端事務請求轉換成一個事務Proposal (提議),並將該Proposal 分發給集羣中所有的Follower服務器。之後Leader 服務器需要等待所有Follower 服務器的反饋,一旦超過半數的Follower服務器進行了正確的反饋後,那麼Leader就會再次向所有的Follower服務器分發Commit消息,要求其將前一個Proposal 進行提交。

二、協議內容

ZAB協議包括兩種基本的模式,分別是崩潰恢復和消息廣播。

什麼時候會進行Leader選舉?
當整個服務框架在啓動過程中,或是當Leader服務器出現網絡中斷、崩潰退出與重啓等異常情況時,ZAB協議就會進入恢復模式並選舉產生新的Leader 服務器。

什麼時候進入消息廣播模式 和 數據恢復模式?

當選舉產生了新的Leader服務器,同時集羣中已經有過半的機器與該Leader服務器完成了狀態同步之後,ZAB協議就會退出恢復模式。其中,所謂的狀態同步是指數據同步,用來保證集羣中存在過半的機器能夠和Leader服務器的數據狀態保持一致。
當集羣中已經有過半的Follower 服務器完成了和Leader 服務器的狀態同步,那麼整個服務框架就可以進入消息廣播模式了。當一臺同樣遵守ZAB協議的服務器啓動後加入到集羣中時,如果此時集羣中已經存在一個Leader服務器在負責進行消息廣播,那麼新加入的服務器就會自覺地進入數據恢復模式:找到Leader 所在的服務器,並與其進行數據同步,然後一起參與到消息廣播流程中去。

當Leader服務器出現崩潰退出或機器重啓,亦或是集羣中已經不存在過半的服務器與該Leader服務器保持正常通信時,那麼在重新開始新一輪的原子廣播事務操作之前,所有進程首先會使用崩潰恢復協議來使彼此達到一個一致的狀態,於是整個ZAB流程就會從消息廣播模式進入到崩潰恢復模式。
一個機器要成爲新的Leader,必須獲得過半進程的支持,同時由於每個進程都有可能會崩潰,因此,在ZAB協議運行過程中,前後會出現多個Leader, 並且每個進程也有可能會多次成爲Leader.進入崩潰恢復模式後,只要集羣中存在過半的服務器能夠彼此進行正常通信,那麼就可以產生一個新的Leader並再次進人消息廣播模式。

只有Leader能處理事務,如果其他節點收到事務請求呢?
ZooKeeper 設計成只允許唯一的一個Leader服務器來進行事務請求的處理。Leader服務器在接收到客戶端的事務請求後,會生成對應的事務提案併發起一輪廣播協議,而如果集羣中的其他機器接收到客戶端的事務請求,那麼這些非Leader服務器會首先將這個事務請求轉發給
Leader服務器。

消息廣播

ZAB協議的消息廣播過程使用的是一個原子廣播協議,類似於一個二階段提交過程。針對客戶端的事務請求,Leader 服務器會爲其生成對應的事務Proposal, 並將其發送給集羣中其餘所有的機器,然後再分別收集各自的選票,最後進行事務提交,如圖所示就是ZAB協議消息廣播流程的示意圖。
在這裏插入圖片描述

ZAB協議中涉及的二階段提交過程的不同之處?

在ZAB協議的二階段提交過程中,移除了中斷邏輯,所有的Follower 服務器要麼正常反饋Leader提出的事務Proposal,要麼就拋棄Leader服務器。同時,ZAB協議將二階段提交中的中斷邏輯移除意味着我們可以在過半的Follower 服務器已經反饋Ack之後就開始提交事務Proposal 了,而不需要等待集羣中所有的Follower服務器都反饋響應。當然,在這種簡化了的二階段提交模型下,是無法處理Leader 服務器崩潰退出而帶來的數據不一致問題的,因此在ZAB協議中添加了另一個模式,即採用崩潰恢復模式來解決這個問題。另外,整個消息廣播協議是基於具有FIFO(先進先出)特性的TCP協議來進行網絡通信的,因此能夠很容易地保證消息廣播過程中消息接收與發送的順序性。

什麼是ZXID?

在整個消息廣播過程中,Leader服務器會爲每個事務請求生成對應的Proposal來進行廣播,並且在廣播事務Proposal之前,Leader服務器會首先爲這個事務Proposal分配一個全局單調遞增的唯一ID, 我們稱之爲事務ID (即ZXID)。

由於ZAB協議需要保證每一個消息嚴格的因果關係,因此必須將每一個事務Proposal按照其ZXID的先後順序來進行排序與處理。具體的,在消息廣播過程中,Leader服務器會爲每一個Follower服務器都各自分配一個單獨的隊列,然後將需要廣播的事務Proposal 依次放入這些隊列中去,並且根據FIFO策略進行消息發送。每一個Follower服務器在接收到這個事務Proposal 之後,都會首先將其以事務日誌的形式寫入到本地磁盤中去,並且在成功寫入後反饋給Leader服務器一個Ack響應。當Leader服務器接收到超過半數Follower的Ack響應後,就會廣播一個Commit消息給所有的Follower服務器以通知其進行事務提交,同時Leader自身也會完成對事務的提交,而每一個 Follower服務器在接收到Commit消息後,也會完成對事務的提交。

崩潰恢復

什麼時候進入崩潰恢復模式?

ZAB協議的這個基於原子廣播協議的消息廣播過程,在正常情況下運行非常良好,但是一旦Leader服務器出現崩潰,或者說由於網絡原因導致Leader服務器失去了與過半Follower的聯繫,那麼就會進入崩潰恢復模式。

在ZAB協議中,爲了保證程序的正確運行,整個恢復過程結束後需要選舉出一個新的Leader服務器。因此,ZAB協議需要一個高效且可靠的Leader選舉算法,從而確保能夠快速地選舉出新的Leader。同時,Leader選舉算法不僅僅需要讓Leader自己知道其自身已經被選舉爲Leader,同時還需要讓集羣中的所有其他機器也能夠快速地感知到選舉產生的新的Leader服務器。

基本特性
根據上面的內容,我們瞭解到,ZAB協議規定了如果一個事務Proposal在一臺機器上被處理成功,那麼應該在所有的機器上都被處理成功,哪怕機器出現故障崩潰。接下來我們看看在崩潰恢復過程中,可能會出現的兩個數據不一致性的隱患及針對這些情況ZAB協議所需要保證的特性。

特性一:
ZAB協議需要確保那些已經在Leader服務器.上提交的事務最終被所有服務器都提交。

假設一個事務在Leader服務器上被提交了,並且已經得到過半Follower服務器的Ack反饋,但是在它將Commit消息發送給所有Follower機器之前,Leader服務器掛了
在這裏插入圖片描述
上圖中的消息C2就是一個典型的例子:在集羣正常運行過程中的某一個時刻,Leader服務器先後廣播了消息PI、P2、C1、P3和C2,其中,當Leader服務器將消息C2(C2是CommitOfProposal2的縮寫,即提交事務Proposal2)發出後就立即崩潰退出了。針對這種情況,ZAB協議就需要確保事務Proposal2最終能夠在所有的服務器上都被提交成功,否則將出現不一致。

特性二:
ZAB協議需要確保丟棄那些只在Leader服務器上被提出的事務。

如果在崩潰恢復過程中出現一個需要被丟棄的提案,那麼在崩潰恢復結束後
需要跳過該事務Proposal,如下圖。
在這裏插入圖片描述

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

結合上面提到的這兩個崩潰恢復過程中需要處理的特殊情況,就決定了ZAB協議必須設計這樣一個Leader 選舉算法:
能夠確保提交已經被Leader 提交的事務Proposal, 同時丟棄已經被跳過的事務Proposal。

針對這個要求,如果讓Leader選舉算法能夠保證新選舉出來的Leader服務器擁有集羣中所有機器最高編號(即ZXID最大)的事務Proposal,那麼就可以保證這個新選舉出來的Leader一定具有所有已經提交的提案。因爲每次提交事務ZXID都會自增,更爲重要的是,如果讓具有最高編號事務Proposal 的機器來成爲Leader, 就可以省去Leader 服務器檢查Proposal的提交和丟棄工作的這一步操作了。

三、數據同步

完成Leader選舉之後,在正式開始工作(即接收客戶端的事務請求,然後提出新的提案)之前,Leader服務器會首先確認事務日誌中的所有Proposal是否都已經被集羣中過半的機器提交了,即是否完成數據同步。

ZAB協議的數據同步過程

所有正常運行的服務器,要麼成爲Leader,要麼成爲Follower並和Leader保持同步。Leader服務器需要確保所有的Follower服務器能夠接收到每一條事務Proposal,並且能夠正確地將所有已經提交了的事務Proposal應用到內存數據庫中去。具體的,Leader服務器會爲每一個Follower服務器都準備一個隊列,並將那些沒有被各Follower服務器同步的事務以Proposal消息的形式逐個發送給Follower服務器,並在每一個Proposal消息後面緊接着再發送一個Commit消息,以表示該事務已經被提交。等到Follower服務器將所有其尚未同步的事務Proposal都從Leader服務器上同步過來併成功應用到本地數據庫中後,Leader服務器就會將該Follower服務器加入到真正的可用Follower列表中,並開始之後的其他流程。上面講到的就是正常情況下的數據同步邏輯。

ZAB協議是如何處理那些需要被丟棄的事務Proposal 的。

在ZAB協議的事務編號ZXID設計中,ZXID是一個64位的數字,其中低32位可以看作是一個簡單的單調遞增的計數器,針對客戶端的每一個事務請求,Leader服務器在產生一個新的事務Proposal的時候,都會對該計數器進行加1操作;而高32位則代表了Leader週期epoch的編號,每當選舉產生一個新的Leader服務器,就會從這個Leader服務器上取出其本地日誌中最大事務Proposal的ZXID,並從該ZXID中解析出對應的epoch值,然後再對其進行加1操作,之後就會以此編號作爲新的epoch,並將低32位置0來開始生成新的ZXID。ZAB協議中的這一通過epoch編號來區分Leader週期變化的策略,能夠有效地避免不同的Leader服務器錯誤地使用相同的ZXID編號提出不一樣的事務Proposal的異常情況,這對於識別在Leader崩潰恢復前後生成的Proposal非常有幫助,大大簡化和提升了數據恢復流程。基於這樣的策略,當一個包含了上一個Leader 週期中尚未提交過的事務Proposal 的服務器啓動時,其肯定無法成爲Leader, 原因很簡單,因爲當前集羣中一定包含一個Quorum集合,該集合中的機器一定包含 了更高epoch的事務Proposal, 因此這臺機器的事務Proposal肯定不是最高,也就無法成爲Leader 了。當這臺機器加入到集羣中,以Follower角色連接上Leader服務器之後,Leader服務器會根據自己服務器上最後被提交的Proposal 來和Follower服務器的Proposal進行比對,比對的結果當然是Leader 會要求Follower進行一個回退操作一回退到一個確實已經被集羣中過半機器提交的最新的事務Proposal。 舉個例子來說,在上圖中,當Serverl連接上Leader後,Leader 會要求Serverl去除P3。

四、ZAB算法描述

整個ZAB協議主要包括消息廣播和崩潰恢復兩個過程,進一步可以細分爲三個階段,分別是發現(Discovery).同步(Synchronization) 和廣播( Broadcast)階段。組成ZAB協議的每一個分佈式進程,會循環地執行這三個階段,我們將這樣一個循環稱爲一個主進程週期。

階段一:發現

階段-.主要就是Leader選舉過程,用於在多個分佈式進程中選舉出主進程,準Leader L和Follower F的工作流程分別如下。

步驟F.1.1 Follower F將自己最後接受的事務Proposal 的epoch值CEPOCH發送給準Leader L。

步驟L.1.1當接收到來 自過半Follower 的epoch值消息後,準Leader L會生成新的epoch值消息給這些過半的Follower。
關於這個epoch值e’,準Leader L會從所有接收到的epoch值消息中選取出最大的epoch值,然後對其進行加1操作,即爲e’。

步驟F.1.2當Follower接收到來自準Leader L的新的epoch值消息後,如果其檢測到當前的epoch值小於e’,那麼就會將當前epoch值賦值爲e’,同時向這個準Leader L反饋Ack消息。在這個反饋消息(ACK-E(F.p,hr))中,包含了當前該Follower的最後一個處理的事務,以及該Follower的歷史事務Proposal集合。當LeaderL接收到來自過半Follower的確認消息Ack之後,LeaderL就會從這過半服務器中選取出一個FollowerF,並使用其作爲初始化事務集合le’。
關於這個FollowerF的選取,對於Quorum中其他任意-一個FollowerF’,F需要滿足以下兩個條件中的一個:
CEPOCH(F’p) < CEPOCH (F.p) 需要小於最後一個提交事務的epoch值。
(CEPOCH (F’p)= CEPOCH(F.p))&(F’zxid <F.zxid或F’zxid= F.zxid) 需要等於最後一個提交事務的epoch值並且zxid小於Follower f處理過的歷史事務Proposal中最後一個事務Proposal 的事務標識ZXID。
至此,ZAB協議完成階段一的工作流程。.

階段二:同步

在完成發現流程之後,就進入了同步階段。在這一階段中,Leader L和FollowerF的工
作流程分別如下。

步驟L.2.1 Leader L會將e’(最大的epcho值)和Ie’(初始化事務集合)以NEWLEADER(e’,Ie’)消息的形式發送給所有Quorum(認定整個集羣是否可用的一種方式)中的Follower。

步驟F.2.1當Follower 接收到來自Leader L的NEWLEADER(e’,le’)消息後,如果Follower發現CEPOCH (F.p) ≠e’, 那麼直接進入下一輪循環,因爲此時Follower發現自己還在上一輪,或者更上輪,無法參與本輪的同步。如果CEPOCH (F.p)= e’,那麼Follower就會執行事務應用操作。最後,Follower 會反饋給Leader,表明自己已經接受並處理了所有L中的事務Proposal。

步驟L.2.2當Leader接收到來自過半Follower 針對NEWLEADER(e’,Ie’)的反饋消息後,就會向所有的Follower發送Commit消息。至此Leader完成階段二。

步驟F.2.2當Follower收到來自Leader的Commit消息後,就會依次處理並提交所有在Ie’中未處理的事務。至此Follower完成階段二。

階段三:廣播
完成同步階段之後, ZAB協議就可以正式開始接收客戶端新的事務請求,並進行消息廣播流程。

步驟L.3.1 Leader L接收到客戶端新的事務請求後,會生成對應的事務Proposal,並根據ZXID的順序向所有Follower發送提案<e’,<v,z>>,其中epoch(z) = e’

步驟F.3.1Follower根據消息接收的先後次序來處理這些來自Leader的事務Proposal,並將他們追加到hp中去,之後再反饋給Leader。

步驟L.3.1當Leader 接收到來自過半Follower 針對事務Proposal <e’,<v,z>>的Ack消息後,就會發送Commit <e’,<v,z>>消息給所有Follower,要求它們進行事務的提交。
步驟F.3.2當Follower F接收到來自Leader的Commit <e’,<v,z>>消息後,就會開始提交事務Proposal<e’,<v,z>>。 需要注意的是,此時該FollowerF必定已經提交了事務Proposal<v’,z’>
以上就是整個ZAB協議的三個核心工作流程。

五、ZAB 與Paxos算法的聯繫與區別

ZAB協議並不是Paxos算法的一個典型實現,在說ZAB和Paxos之間的區別之前,我們首先來看下兩者的聯繫。

  • 兩者都存在一個類似於Leader進程的角色,由其負責協調多個Follower進程的運行。
  • Leader進程都會等待超過半數的Follower做出正確的反饋後,纔會將一個提案進行提交。
  • 在ZAB協議中,每個Proposal中都包含了一個epoch值,用來代表當前的Leader週期,在Paxos算法中,同樣存在這樣的一個標識,只是名字變成了Ballot。

在Paxos算法中,一個新選舉產生的主進程會進行兩個階段的工作。第一階段被稱爲讀階段,在這個階段中,這個新的主進程會通過和所有其他進程進行通信的方式來收集上一個主進程提出的提案,並將它們提交。第二階段被稱爲寫階段,在這個階段,當前主進程開始提出它自己的提案。在Paxos算法設計的基礎上,ZAB協議額外添加了一個同步階段。在同步階段之前,ZAB協議也存在一個和Paxos算法中的讀階段非常類似的過程,稱爲發現(Discovery)階段。在同步階段中,新的Leader會確保存在過半的Follower已經提交了之前Leader週期中的所有事務Proposal。 這一同步階段的引入,能夠有效地保證Leader在新的週期中提出事務Proposal之前,所有的進程都已經完成了對之前所有事務Proposal的提交。一旦完成同步階段後,那麼ZAB就會執行和Paxos算法類似的寫階段。總的來講,ZAB協議和Paxos 算法的本質區別在於,兩者的設計目標不太一樣。

不同點總結:
ZAB協議主要用於構建一個高可用的分佈式數據主備系統,例如ZooKeeper
Paxos算法則是用於構建一一個分佈式的一致性狀態機系統。

六、運行狀態

在ZAB協議的設計中,每-一個進程都有可能處於以下三種狀態之一。

  • LOOKING: Leader選舉階段
  • FOLLOWING: Follower 服務器和Leader保持同步狀態
  • LEADING:Leader服務器作爲主進程領導狀態
發佈了68 篇原創文章 · 獲贊 12 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章