Raft 爲什麼是更易理解的分佈式一致性算法

一致性問題可以算是分佈式領域的一個聖殿級問題了,關於它的研究可以回溯到幾十年前。

拜占庭將軍問題

Leslie Lamport 在三十多年前發表的論文《拜占庭將軍問題》(參考[1])。

拜占庭位於如今的土耳其的伊斯坦布爾,是東羅馬帝國的首都。由於當時拜占庭羅馬帝國國土遼闊,爲了防禦目的,因此每個軍隊都分隔很遠,將軍與將軍之間只能靠信差傳消息。在戰爭的時候,拜占庭軍隊內所有將軍必需達成 一致的共識,決定是否有贏的機會纔去攻打敵人的陣營。但是,在軍隊內有可能存有叛徒和敵軍的間諜,左右將軍們的決定又擾亂整體軍隊的秩序,在進行共識時,結果並不代表大多數人的意見。這時候,在已知有成員不可靠的情況下,其餘忠誠的將軍在不受叛徒或間諜的影響下如何達成一致的協議,拜占庭問題就此形成。拜占庭假設是對現實世界的模型化,由於硬件錯誤、網絡擁塞或斷開以及遭到惡意攻擊,計算機和網絡可能出現不可預料的行爲。

Lamport 一直研究這類問題,發表了一系列論文。但綜合總結一下就是回答下面三個問題:

  1. 類似拜占庭將軍這樣的分佈式一致性問題是否有解?
  2. 如果有解的話需要滿足什麼樣的條件?
  3. 在特定前提條件的基礎上,提出一種解法。

前兩個問題 Lamport 在論文《拜占庭將軍問題》已經回答,而第三個問題在後來的論文 《The Part-Time Parliament》中提出了一種算法並命名爲 Paxos。這篇論文使用了大量的數學證明,而我基本就看不懂了(數學符號都認不全-。-;),考慮到大家理解起來都比較困難,後來 Lamport 又寫了另外一篇論文 《Paxos Made Simple》完全放棄了所有數學符號的證明,使用純英文的邏輯推導。我勉強逐字看了一遍,然後感覺若有所悟,但你問我搞懂了嗎,我的標準應該還是沒懂。對我來說理解一個算法有個明確的標準,就是真的懂了會在頭腦裏能將算法映射爲代碼,而看完後面一篇論文僅僅是若有所悟還達不到能映射爲代碼的清晰度。

雖然 Lamport 認爲 Paxos 很 simple,但也許只是針對他的頭腦而言。事實是大家理解起來都還是很困難,所以 Raft 就是建立在希望得到一個更易於理解的 Paxos 算法的替代品。把可理解性作爲算法的主要目標之一,從論文題目就可看出來《In Search of an Understandable Consensus Algorithm》。

在進入正題前,我想起一箇舊故事可以很直觀的感受對一個問題不同的思維視角在可理解性上的差異。

不同視角的可理解性

依稀記得大約在二十年前,我還在讀初中時在一本可能大概叫《數學中的發散思維》(不能很清晰記得書名了)的書中看到這麼一個有趣的問題。

甲乙兩人輪流在一張圓桌上平放黑白圍棋子,每次放一子,棋子不許重疊,誰先沒有地方放就輸。
請問怎樣放才能贏?

這個問題有兩層意思,第一,有沒有一種放法保證必贏?第二,如果有怎麼證明?這裏先停頓下,思考十秒鐘。

上面的圖回答了這個問題,就是先行者必勝,這裏使用了三種不同的思維方式。

  1. 假如桌子只有一個圍棋子那麼大。
  2. 假如桌子無限大,先行者先佔住圓心,由於圓是對稱圖形,所以只要對手還能找到位置放,你總能在對稱的另一面找到位置放。
  3. 一個圓中可畫單數個直徑相等且互切的小圓。

三種不同的思維方式在可理解性難度上逐漸加深。第一種是極簡化思維,但數學上是不嚴謹的。第二種是極限思維,和第一種結合起來就是數學歸納法了,在數學上是嚴謹的。第三種是形象思維,使用了幾何學概念,但對於沒有幾何學基礎知識的人就很難理解了。

Raft 協議的易理解性描述

雖然 Raft 的論文比 Paxos 簡單版論文還容易讀了,但論文依然發散的比較多,相對冗長。讀完後掩卷沉思覺得還是整理一下才會更牢靠,變成真正屬於自己的。這裏我就藉助前面黑白棋落子裏第一種極簡思維來描述和概念驗證下 Raft 協議的工作方式。

在一個由 Raft 協議組織的集羣中有三類角色:

  1. Leader(領袖)
  2. Follower(羣衆)
  3. Candidate(候選人)

就像一個民主社會,領袖由民衆投票選出。剛開始沒有領袖,所有集羣中的參與者都是羣衆,那麼首先開啓一輪大選,在大選期間所有羣衆都能參與競選,這時所有羣衆的角色就變成了候選人,民主投票選出領袖後就開始了這屆領袖的任期,然後選舉結束,所有除領袖的候選人又變回羣衆角色服從領袖領導。這裏提到一個概念「任期」,用術語 Term 表達。關於 Raft 協議的核心概念和術語就這麼多而且和現實民主制度非常匹配,所以很容易理解。三類角色的變遷圖如下,結合後面的選舉過程來看很容易理解。

Leader 選舉過程

在極簡的思維下,一個最小的 Raft 民主集羣需要三個參與者(如下圖:A、B、C),這樣纔可能投出多數票。初始狀態 ABC 都是 Follower,然後發起選舉這時有三種可能情形發生。下圖中前二種都能選出 Leader,第三種則表明本輪投票無效(Split Votes),每方都投給了自己,結果沒有任何一方獲得多數票。之後每個參與方隨機休息一陣(Election Timeout)重新發起投票直到一方獲得多數票。這裏的關鍵就是隨機 timeout,最先從 timeout 中恢復發起投票的一方向還在 timeout 中的另外兩方請求投票,這時它們就只能投給對方了,很快達成一致。

選出 Leader 後,Leader 通過定期向所有 Follower 發送心跳信息維持其統治。若 Follower 一段時間未收到 Leader 的心跳則認爲 Leader 可能已經掛了再次發起選主過程。

Leader 節點對一致性的影響

Raft 協議強依賴 Leader 節點的可用性來確保集羣數據的一致性。數據的流向只能從 Leader 節點向 Follower 節點轉移。當 Client 向集羣 Leader 節點提交數據後,Leader 節點接收到的數據處於未提交狀態(Uncommitted),接着 Leader 節點會併發向所有 Follower 節點複製數據並等待接收響應,確保至少集羣中超過半數節點已接收到數據後再向 Client 確認數據已接收。一旦向 Client 發出數據接收 Ack 響應後,表明此時數據狀態進入已提交(Committed),Leader 節點再向 Follower 節點發通知告知該數據狀態已提交。

在這個過程中,主節點可能在任意階段掛掉,看下 Raft 協議如何針對不同階段保障數據一致性的。

1. 數據到達 Leader 節點前

這個階段 Leader 掛掉不影響一致性,不多說。

2. 數據到達 Leader 節點,但未複製到 Follower 節點

這個階段 Leader 掛掉,數據屬於未提交狀態,Client 不會收到 Ack 會認爲超時失敗可安全發起重試。Follower 節點上沒有該數據,重新選主後 Client 重試重新提交可成功。原來的 Leader 節點恢復後作爲 Follower 加入集羣重新從當前任期的新 Leader 處同步數據,強制保持和 Leader 數據一致。

3. 數據到達 Leader 節點,成功複製到 Follower 所有節點,但還未向 Leader 響應接收

這個階段 Leader 掛掉,雖然數據在 Follower 節點處於未提交狀態(Uncommitted)但保持一致,重新選出 Leader 後可完成數據提交,此時 Client 由於不知到底提交成功沒有,可重試提交。針對這種情況 Raft 要求 RPC 請求實現冪等性,也就是要實現內部去重機制。

4. 數據到達 Leader 節點,成功複製到 Follower 部分節點,但還未向 Leader 響應接收

這個階段 Leader 掛掉,數據在 Follower 節點處於未提交狀態(Uncommitted)且不一致,Raft 協議要求投票只能投給擁有最新數據的節點。所以擁有最新數據的節點會被選爲 Leader 再強制同步數據到 Follower,數據不會丟失並最終一致。

5. 數據到達 Leader 節點,成功複製到 Follower 所有或多數節點,數據在 Leader 處於已提交狀態,但在 Follower 處於未提交狀態

這個階段 Leader 掛掉,重新選出新 Leader 後的處理流程和階段 3 一樣。

6. 數據到達 Leader 節點,成功複製到 Follower 所有或多數節點,數據在所有節點都處於已提交狀態,但還未響應 Client

這個階段 Leader 掛掉,Cluster 內部數據其實已經是一致的,Client 重複重試基於冪等策略對一致性無影響。

7. 網絡分區導致的腦裂情況,出現雙 Leader

網絡分區將原先的 Leader 節點和 Follower 節點分隔開,Follower 收不到 Leader 的心跳將發起選舉產生新的 Leader。這時就產生了雙 Leader,原先的 Leader 獨自在一個區,向它提交數據不可能複製到多數節點所以永遠提交不成功。向新的 Leader 提交數據可以提交成功,網絡恢復後舊的 Leader 發現集羣中有更新任期(Term)的新 Leader 則自動降級爲 Follower 並從新 Leader 處同步數據達成集羣數據一致。

綜上窮舉分析了最小集羣(3 節點)面臨的所有情況,可以看出 Raft 協議都能很好的應對一致性問題,並且很容易理解。

總結

就引用 Raft 論文最後的一節的綜述來總結本文吧。

算法以正確性、高效性、簡潔性作爲主要設計目標。
雖然這些都是很有價值的目標,但這些目標都不會達成直到開發者寫出一個可用的實現。
所以我們相信可理解性同樣重要。

深以爲然,想想 Paxos 算法是 Leslie Lamport 在 1990 年就公開發表在了自己的網站上,想想我們是什麼時候才聽說的?什麼時候纔有一個可用的實現?而 Raft 算法是 2013 年發表的,大家在參考[5]上面可以看到有多少個不同語言開源的實現庫了,這就是可理解性的重要性。

參考

[1]. LESLIE LAMPORT, ROBERT SHOSTAK, MARSHALL PEASE. The Byzantine General Problem. 1982
[2]. Leslie Lamport. The Part-Time Parliament. 1998
[3]. Leslie Lamport. Paxos Made Simple. 2001
[4]. Diego Ongaro and John Ousterhout. Raft Paper. 2013
[5]. Raft Website. The Raft Consensus Algorithm
[6]. Raft Demo. Raft Animate Demo

 

轉自 : https://www.cnblogs.com/mindwind/p/5231986.html

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