前言在Zookeeper集羣中,客戶端會隨機鏈接到Zookeeper集羣中 的一個節點。若是是讀請求,就直接從當前節點中讀取數據,若是是寫請求,那麼請求先會被轉發給leader提交事務。而後leader再廣播事務,只要有超過半數節點寫入成功, 那麼寫請求就會被提交(類2PC事務)。整個過程以下圖所示: 算法
整套數據同步機制是經過ZAB
協議實現的,下面咱們重點了解一下ZAB
協議的實現原理。服務器
ZAB
協議簡介
ZAB
(Zookeeper Atomic Broadcast) 協議是爲Zookeeper專門設計的一種支持崩潰恢復的原子廣播協議。在 ZooKeeper 中,主要依賴ZAB
協議來實現 分佈式數據一致性。ZAB
協議包含兩種基本模式,分別是崩潰恢復和消息廣播。網絡
當整個集羣在啓動時,或者當leader
節點出現網絡中斷、 崩潰等狀況時,ZAB
協議就會進入恢復模式並選舉產生新的leader
,當leader
服務器選舉出來後,而且集羣中有過半的機器和該leader
節點完成數據同步後(同步指的是數據同步,用來保證集羣中過半的機器可以和 leader
服務器的數據狀態保持一致),ZAB
協議就會退出恢復模式。分佈式
當集羣中已經有過半的follower
節點完成了和leader
狀態同步之後,那麼整個集羣就進入了消息廣播模式。這個時候,在leader
節點正常工做時,啓動一臺新的服務器加入到集羣,那這個服務器會直接進入數據恢復模式,和leader
節點進行數據同步。同步完成後便可正常對外提供非事務請求的處理。ide
leader
節點能夠處理事務請求和非事務請求,follower
節點只能處理非事務請求,若是follower
節點接收到非事務請求,會把這個請求轉發給leader
服務器。spa
消息廣播的過程其實是一個 簡化版本的二階段提交過程,具體過程以下:設計
leader
接收到消息請求後,將消息賦予一個全局惟一的64位自增id(zxid
)。leader
爲每一個follower準備了一個FIFO
隊列(經過TCP協議來實現,以實現了全局有序這一個特色)將帶有 zxid
的消息做爲一個提案(proposal
)分發給全部的follower
。follower
接收到proposal
,先把proposal
寫到磁盤,寫入成功之後再向leader
回覆一個ack
。leader
接收到合法數量(超過半數節點)的ACK
後,leader
就會向這些follower
發送commit
命令,同時會在本地執行該消息。follower
收到消息的commit
命令之後,會提交該消息。
zxid
是Zookeeper的事務id,爲了保證事務的順序一致性,zookeeper採用了遞增的事務id號(zxid
)來標識事務。全部的提案(proposal)都在被提出的時候加上了zxid
。日誌
zxid
是一個64 位數字,高32位是epoch
用來標識leader
關係是否改變,每次一個leader被選出來,新leader
都會將epoch
加1,標識當前leader
的統治時期。低32位用於遞增計數。code
epoch
能夠理解爲當前集羣所處的年代或者週期,每一個leader
就像皇帝,都有本身的年號,因此每次改朝換代,leader
變動以後,都會在前一個年代的基礎上加 1。這樣就算舊的leader
崩潰恢復以後,也沒有人聽他的了,由於follower
只遵從當前年代的leader
的命令。blog
前面咱們已經講過了ZAB
協議中的消息廣播過程,在正常狀況下,上述方式是沒有任何問題的,可是一旦leader
節點崩潰,或者因爲網絡問題致使leader
服務器失去了過半的follower
節點的聯繫,那麼Zookeeper集羣就會進入到崩潰恢復模式。崩潰恢復狀態下 zab 協議須要作兩件事,選舉出新的leader和數據同步。
leader
失去與過半follower
節點聯繫,多是leader
節點和follower
節點之間產生了網絡分區,那麼此時的leader
再也不是合法的leader
了。
前面在講解消息廣播時,知道ZAB
協議的消息廣播機制是簡化版本的2PC
協議,這種協議只須要集羣中過半的節點 響應提交便可。可是它沒法處理leader
服務器崩潰帶來的數據不一致問題。所以在 ZAB 協議中添加了一個崩潰恢復模式來解決這個問題。
ZAB
協議中的崩潰恢復須要保證,若是一個事務 Proposal 在一臺機器上被處理成功,那麼這個事務應該在全部機器上都被處理成功,哪怕是出現故障。 爲了達到這 個目的,咱們先來設想一下,在Zookeeper
中會有哪些場 景緻使數據不一致性,以及針對這些場景,zab
協議中的崩潰恢復應該怎麼處理。
當leader
收到合法數量follower
的ACK
後,就向各個follower
廣播COMMIT
命令,同時也會在本地執行COMMIT
並向鏈接的客戶端返回「成功」。可是若是在各個follower
在收到COMMIT
命令前leader
就掛了,致使剩下的服務器並無執行都這條消息。
圖中的C2
就是一個典型的例子,在集羣正常運過程的某一個時刻, Server1
是leader
服務器,前後廣播了消 息P一、P二、C一、P3和C2。其中當leader
服務器把消息C2
(Commit事務proposal2)發出後就當即崩潰退出了,那麼針對這種狀況,ZAB
協議就須要確保事務Proposal2
最終可以在全部的服務器上都能被提交成功,不然將會出現不一致。
當leader
接收到消息請求生成proposal
後就掛了,其餘follower
並無到此proposal
,所以通過恢復模式從新選了leader
後,這條消息是被跳過的。 此時,以前掛了的leader
從新啓動並註冊成了 follower, 他保留了被跳過消息的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
清除。