深入理解Zookeeper一致性協議:Zab協議


分布式一致性算法
分布式一致性算法可以保證多個數據節點上有一致的數據,目前有Paxos、Raft、Zab和Gossip幾種,其中Zookeeper使用的是Zab,而Zab又和Raft比較類似,因此本文主要記錄一下Raft和Zab。
ZAB協議用途
ZAB(Zookeeper Atomic Broadcast) 協議是為分布式協調服務zookeeper專門設計的一種支持崩潰恢復的原子廣播協議。在zookeeper中,主要依賴ZAB協議來實現分布式數據一致性,基於該協議,zookeeper實現了一種主備模式的系統架構來保持集群中各個副本之間的數據一致性。
ZAB協議介紹
ZAB協議包含兩種基本模式,分別是:
1》崩潰恢復之數據恢復
2》消息廣播之原子廣播
當整個集群正在啟動時,或者當leader節點出現網絡中斷、崩潰等情況時,ZAB協議就會進入恢復模式並選舉產生新的leader,當leader服務器選舉出來后,並且集群中有過半的機器和該leader節點完成數據同步后(同步指的是數據同步,用來保證集群中過半的機器能夠和leader服務器的數據狀態保持一致),ZAB協議就會退出恢復模式。
當集群中已經有過半的Follower節點完成了和Leader狀態同步以后,那么整個集群就進入了消息廣播模式。這個時候,在Leader節點正常工作時,啟動一台新的服務器加入到集群,那這個服務器會直接進入數據恢復模式,和leader節點進行數據同步。同步完成后即可正常對外提供非事務請求的處理。
消息廣播(原子廣播)
消息廣播實際上是一個簡化版的2PC提交過程。

過程:
1》leader接收到消息請求后,將消息賦予一個全局唯一的64位自增id,叫:zxid,通過zxid的大小比較就可以實現因果有序這個特征。
2》leader為每個follower准備了一個FIFO隊列(通過TCP協議來實現,以實現全局有序這一個特點)將帶有zxid的消息作為一個提案(proposal)分發給所有的 follower。
3》當follower接收到proposal,先把proposal寫到磁盤,寫入成功以后再向leader回復一個ack。
4》當leader接收到合法數量(超過半數節點)的ack后,leader就會向這些follower發送commit命令,同時會在本地執行該消息。
5》當follower收到消息的commit命令以后,會提交該消息。
崩潰恢復(數據恢復)
ZAB協議的這個基於原子廣播協議的消息廣播過程,在正常情況下是沒有任何問題的,但是一旦Leader節點崩潰,或者由於網絡問題導致Leader服務器失去了過半的Follower節點的聯系(leader失去與過半follower節點聯系,可能是leader節點和 follower節點之間產生了網絡分區,那么此時的leader不再是合法的leader了),那么就會進入到崩潰恢復模式。在ZAB協議中,為了保證程序的正確運行,整個恢復過程結束后需要選舉出一個新的Leader。
為了使leader掛了后系統能正常工作,需要解決以下兩個問題:
1》已經被處理的消息不能丟失
當leader收到合法數量follower的ack后,就向各個follower廣播commit命令,同時也會在本地執行commit並向連接的客戶端返回「成功」。但是如果各個follower在收到commit命令前leader就掛了,導致剩下的服務器並沒有執行到這條消息。

leader對事務消息發起commit操作,該消息在follower1上執行了,但是follower2還沒有收到commit,leader就已經掛了,而實際上客戶端已經收到該事務消息處理成功的回執了。所以在zab協議下需要保證所有機器都要執行這個事務消息,必須滿足已經被處理的消息不能丟失。
2》被丟棄的消息不能再次出現
當leader接收到消息請求生成proposal后就掛了,其他follower並沒有收到此proposal,因此經過恢復模式重新選了leader后,這條消息是被跳過的。 此時,之前掛了的leader重新啟動並注冊成了follower,他保留了被跳過消息的proposal狀態,與整個系統的狀態是不一致的,需要將其刪除。(leader都換代了,所以以前leader的proposal失效了)
針對崩潰恢復的兩種情況分析
ZAB協議需要滿足上面兩種情況,就必須要設計一個leader選舉算法,能夠確保已經被leader提交的事務Proposal能夠提交、同時丟棄已經被跳過的事務Proposal。
針對這個要求:
如果leader選舉算法能夠保證新選舉出來的Leader服務器擁有集群中所有機器最高編號(ZXID 最大)的事務Proposal,那么就可以保證這個新選舉出來的leader一定具有已經提交的提案。因為所有提案被commit之前必須有超過半數的follower ack,即必須有超過半數節點的服務器的事務日志上有該提案的proposal,因此只要有合法數量的節點正常工作,就必然有一個節點保存了所有被commit消息的proposal狀態。
另外一個,zxid是64位,高32位是epoch編號,每經過一次Leader選舉產生一個新的leader,新的leader會將epoch號+1,低32位是消息計數器,每接收到一條消息這個值+1,新leader選舉后這個值重置為0。這樣設計的好處在於老的leader掛了以后重啟,它不會被選舉為leader,因此此時它的zxid肯定小於當前新的leader。當老的leader作為follower接入新的leader后,新的leader會讓它將所有的擁有舊的epoch號的未被commit的proposal清除。
關於ZXID
zxid,也就是事務id,為了保證事務的順序一致性,zookeeper采用了遞增的事務id號(zxid)來標識事務。所有的提議(proposal)都在被提出的時候加上了zxid,實際中zxid是一個64位的數字,它高32位是epoch(ZAB協議通過epoch編號來區分Leader周期變化的策略)用來標識leader關系是否改變,每次一個leader被選出來,它都會有一個新的epoch=(原來的epoch+1),標識當前屬於那個leader的統治時期。低32位用於遞增計數。
epoch:可以理解為當前集群所處的年代或者周期,每個leader就像皇帝,都有自己的年號,所以每次改朝換代,leader變更之后,都會在前一個年代的基礎上加1。這樣就算舊的leader崩潰恢復之后,也沒有人聽他的了,因為follower只聽從當前年代的leader的命令。
測試epoch的變化
要測試epoch的變化可以做一個簡單的實驗:
1》啟動一個zookeeper集群。
2》在 /tmp/zookeeper/VERSION-2 路徑下會看到一個currentEpoch文件,文件中顯示的是當前的epoch。

3》把leader節點停機,這個時候在看currentEpoch會發生變化。 隨着每次選舉新的leader,epoch都會發生變化。
1>先找到leader節點

2>停止leader節點的服務

3>查看當前epoch號的變化(epoch號由原來的6變為7)

總結:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM