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

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

拜占庭將軍問題

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

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

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》。併發

在進入正題前,我想起一箇舊故事能夠很直觀的感覺對一個問題不一樣的思惟視角在可理解性上的差別。分佈式

不一樣視角的可理解性

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

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

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

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

  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


寫點文字,畫點畫兒,「瞬息之間」一切都變了。以爲不錯,可長按或掃描二維碼關注。

相關文章
相關標籤/搜索