Zookeeper協議篇-Paxos算法與ZAB協議

前言

上篇我們系統性的學習了Zookeeper中的系統模型,對節點特性,權限認證以及事件通知Watcher機制相關進行了學習,本篇我們來學習Zookeeper一致性算法和滿足分佈式協調的Zab協議

Paxos算法

Paxos算法是萊斯利*蘭伯特在1990年提出的一種基於消息傳遞並且具有高度容錯特性的一致性算法,是目前公認的解決分佈式問題上最有效的算法之一

拜占庭問題

1982年 ,Lamport與另兩人共同發表了論文提出了一種計算機容錯理論,爲了描述這個理論中的問題,假設了一個問題相關的故事場景,如下:

拜占庭帝國有許多支軍隊,不同軍隊的將軍之間必須制訂一個統一的行動計劃,從而做出進攻或者撤退的決定,同時,各個將軍在地理上都是被分隔開來的,只能依靠軍隊的通訊員來進行通訊。然而,在所有的通訊員中可能會存在叛徒,這些叛徒可以任意篡改消息,從而達到欺騙將軍的目的。

這就是著名的拜占庭問題,而這個問題就和分佈式場景下異步系統和不可靠的協議中達到一致性類似,後來Lamport 在 1990年提出了一個理論上的一致性解決方案,茼時給出了嚴格的數學證明,Paxos算法由此而生。

Paxos算法

我們首先來看一下,對於一個一致性算法來說,我們應該滿足哪幾點:

1.所有提出的提案最終只有一個被選定

2.如果沒有提案,那麼最終沒有任何選定的提案

3.當提案被選定後,所有客戶端都應該能獲取到提案信息

在Paxos算法中,有Proposer、Acceptor以及Learner三種角色,彼此之間通過收發消息來實現通信。在分佈式場景下,Acceptor絕不可能只有一個實例存在,防止出現單機故障,因此我們需要滿足,多個Acceptor的情況下完成選舉操作,因此我們可以指定Proposer都向一個Acceptor集合中發送提案,而這個集合中的所有的Acceptor都可能批准提案,當有足夠多的Acceptor批准提案的時候,我們就認爲這個提案被選定,而所謂的足夠多,只需要滿足當前Acceptor集合中的大半Acceptor批准提案即可,並且我們規定每個Acceptor在一次選舉過程中,只可以批准一個提案。

算法過程

整個Paxos算法的過程可以總結爲兩階段提交的算法執行過程,分爲Prepare請求階段Accept請求階段,大體的過程如下:

Prepare階段:

Proposer會選擇一個提案,並且編號爲M0,然後向Acceptor集合中發送M0編號的Prepare請求,如果這個時候Acceptor集合中的某個Acceptor收到了這個請求,並且編號M0比當前已經相應的所有的提案的最大編號還要大,那麼Acceptor就需要反饋自己處理的最大編號,並且不會再去響應低於M0編號的任何請求。如果沒有響應過請求,則會直接響應當前的M0編號的請求。

Accept階段:

同樣的,當Proposer發出的提案請求收到了來自整個Acceptor集合中的過半Acceptor的響應,那麼就代表該提案可以生成,這個時候如果響應中過半是無任何提案信息的,則代表當前的提案的取值可以是任意值,如果響應中過半是其他的提案信息,那麼則從中找到最大編號的提案的值,這裏稱爲V0,組成[M0,V0]Acceptor請求,再次發送給整個Acceptor集合。這個時候Acceptor如果沒有通過大於v4sp 編號的提案,那麼則會視爲自動同意該提案,同樣如果得到了過半數的同意,則當前提案視爲通過。

當然在整個過程中,每個Proposer都有自己的週期定時生成不同的提案,並且嚴格按照上述過程運行,最終一定能保證算法的正確性,同樣的,如果Proposer已經要生成更大編號的提案,Accept將收到的最大的編號信息通知給上一個同意的最大提案的Proposer,讓其放棄自己的提案,選擇最新的最大編號的提案即可。

Zab協議

看了前面的Paxos算法,我們可能會認爲Zookeeper就是基於Paxos算法的實現,但是事實上,Zookeeper並不是完全採用的Paxos,而是一種名爲Zookeeper Atomic Broadcast,簡稱ZAB協議的一種支持奔潰恢復的協議作爲數據一致性核心算法。ZAB協議定義整個Zookeeper中關於事物消息的處理流程,大致如下:

所有的Zookeeper處理的事物請求必須僅有一個機器來協調處理,這樣的機器被稱之爲Leader服務器,而剩下的其他Zookeeper則稱之爲Follower,而Leader服務器則是會將客戶端的事物請求發給所有的Follwer服務器,等待所有的Follwer服務器的反饋,一旦接受到了超過半數的Follwer服務器的正常完成事物處理的反饋後,Leader服務器就會再次發送一個Commit消息給所有的Follwer服務器,要求將剛剛的事物請求進行提交

而ZAB協議有兩種基本的模式,分別爲崩潰恢復消息廣播。當整個框架在運行的過程中,或者是當Leader出現網絡中斷、崩潰重啓等異常的情況的時候,ZAB協議就會進入恢復模式,來重新選舉一個新的Leader服務器來處理事物請求,當新的Leader服務器選擇出來,並且和過半以上的機器實現了狀態同步後,ZAB的恢復模式就結束了。這個時候就可以進行ZAB的消息廣播模式了,如果在這個過程中有一個新的Zookeeper加入了當前的集羣中,就會啓動恢復模式,直到實現了和Leader的狀態同步爲止。

正如上述事物處理的過程一樣,Zookeeper中只有Leader可以協調處理事物請求,那麼其他的Follower服務器是不是沒用了呢?當然不是,在整個Zookeeper集羣中,所有的服務器都可以提供對外的請求處理,唯一的區別在於,當Follower服務器接受到了事物請求以後,會轉發給Leader,讓Leader進行統一處理和協調而已。當然在整個集羣的運行過程中會出現很多意外,例如有半數以上的Follower服務器無法與Leader進行正常通信,就會從消息廣播模式進入到崩潰恢復模式,從其他的機器中選舉一個新的Leader出來,同樣的一個Leader的選舉必須得到超過半數的機器的支持,所以當整個集羣的正常服務的機器超過半數,都可以不停的模式轉換,保證集羣服務的穩定性,一旦出現問題的機器數量超過了集羣的半數,整個集羣就不再對外提供事物請求的處理,進入保護模式,僅僅可讀。

消息廣播

ZAB協議中的消息廣播使用的是一個原子廣播協議,整體的過程可以看爲是一個二階段提交的過程。客戶端的事物請求到來以後,Leader服務器會生成對應的事物Proposal,並且將這個發送給當前集羣範圍內的其餘的所有的機器,並且收集來自所有Follower機器的響應選票信息,而二階段的過程的具體體現,移除了傳統二階段的中斷邏輯處理,因此在收集所有Follower機器的響應過程中,Leader不需要等待所有機器響應後才執行第二階段,而是隻需要等待半數以上的機器返回對應的響應,即可開啓第二階段。當然在這種簡化的二階段過程中,有可能會因爲Leader突然奔潰,導致數據不一致的情況,當然出現這種情況就需要啓動崩潰恢復機制來處理,並且所有的消息都是具有FIFO特性的TCP協議來進行網絡通信的,從而保證消息廣播過程中的順序性。

而在第一階段,Leader機器會將事物消息生成對應的Proposal進行廣播,並且會生成一個單調遞增的ID,作爲事物的ID(ZXID),由於ZAB協議需要保證事物嚴格的上下順序性,所以會嚴格按照ZXID的先後來處理對應的消息。而在消息廣播發起後,Leader會爲每一個Follower服務器分配一個單獨的隊列,然後將需要廣播出去的事物Proposal依次存放進這個FIFO的隊列中,每個Follower機器收到事物消息後,會按照事物日誌的方式寫入,成功後反饋給Leader機器Ack響應,當收到半數以上的Ack響應後,Leader就會發起第二階段的Commit消息給所有的Follower機器,並且這個時候Leader也會和Follower一樣進行本地事物的提交操作,完成整個消息的傳遞和提交

崩潰恢復

前面我們提到過,崩潰恢復機制保障了Leader機器在突然異常的情況下,依然能保障一個事物如果在一臺機器上處理成功,那麼就應該被所有的機器處理成功。因此ZAB協議的崩潰恢復機制整體需要保障以下兩點:

1.所有已經在Leader服務器上提交成功的事物最終要被所有的服務器提交

2.如果Leader在消息廣播的第一階段提出的消息,但是並沒有提交,ZAB需要保證丟棄掉這部分事物消息

因此ZAB協議必須設計一個選舉算法,保證提交已經被Leader提交的Proposal,同時跳過僅僅是Leader發起的沒有提交需要跳過的事物Proposal。所以爲了設計選舉出來的Leader是擁有集羣內機器的最高編號(ZXID最大),那麼就可以認爲選舉出來的Leader有所有已經被提交的提案。

在完成了選舉以後,崩潰恢復過程還未結束,此時進入了數據同步過程,Leader需要確認事物日誌中的所有的Proposal被過半以上的Follower提交了,纔算完成數據同步。前面我們知道Leader服務器會將所有的Follower準備一個FIFO的隊列,將所有的需要提交的事物存進去,並且通知Follower,當隊列裏面所有的日誌都已經被Follower提交清空,這個時候代表數據同步完成,該Follower會被加入到可用機器的列表中,直到列表中的可用機器數過半,就會直接開始完成崩潰恢復過程,轉換模式爲消息廣播,對外提供Zookeeper服務。

同樣的,Leader中的已經提交的事物完成同步了,那麼未提交的廢棄事物怎麼剔除呢?在ZAB協議的事物編號ZXID設計中,是一個64位的數字,整體分爲低32位和高32位,低32位可以看成一個單調遞增的計數器,每一個事物消息生成ID前,低32位都會進行原子的+1操作。而ZXID的高32位代表了epoch的編號,而epoch則是每一次選舉出新的Leader的過程中,會從所有的事物ZXID中獲取最大的,然後解析出對應的高32位epoch的值,然後將其+1,代表了選舉的次數,即Leader的生命週期,並且每一次選舉後,epoch+1以後,會將低32位的值清零,作爲新的ZXID值。因此當選舉觸發過程中,有部分機器的ZXID所代表的epoch值較低的時候,這些機器直接會成爲Follower,只會有epoch最大並且一樣的機器之間進行選舉出Leader,而當Leader選舉出來以後,所有的Follower連接上Leader以後,會和Leader比較當前最大的Proposal,這個時候Leader根據比較情況要求回滾或者同步Proposal到當前集羣中過半機器都提交的最大事物Proposal,至此數據同步操作完成,崩潰恢復模式結束。

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