前言
Raft
也是一個 一致性演算法,和 Paxos
目標相同。但它還有另一個名字 - 易於理解的一致性演算法。Paxos
和 Raft
都是為了實現 一致性 產生的。這個過程如同選舉一樣,參選者 需要說服 大多數選民 (伺服器) 投票給他,一旦選定後就跟隨其操作。Paxos
和 Raft
的區別在於選舉的 具體過程 不同。
正文
小試牛刀
在進入正題前,給大家分享一個《數學發散思維》中的一個故事,站在不同思維角度上,瞭解對一個問題理解的差異性。
問題: 甲乙兩人輪流在一張圓桌上平放黑白圍棋子,每次放一子,棋子不許重疊,誰先沒有地方放就輸。請問怎樣放才能贏?
這個問題有兩層意思,第一,有沒有一種放法保證必贏?第二,如果有怎麼證明?
上圖回答了這個問題,那就是先行者必勝,這裡使用了三種不同的思維方式來闡述:
-
假如桌子只有一個圍棋子那麼大。
-
假如桌子無限大,先行者先佔住圓心。由於圓是對稱圖形,所以只要對手還能找到位置放,你總能在對稱的另一面找到位置放。
-
一個圓中可畫單數個直徑相等且互切的小圓。
三種不同的思維方式在可理解性難度上逐漸加深。
-
第一種是 極簡化思維,但數學上是 不嚴謹 的。
-
第二種是 極限思維,和第一種結合起來就是 數學歸納法,在數學上是 嚴謹 的。
-
第三種是 形象思維,使用了 幾何學概念,但對於沒有幾何學基礎知識的人就很難理解了。
什麼是Raft協議
Raft
協議將 Server
程式分成三類,分別是 Leader
,Candidate
,Follower
。一個 Server
程式在某一時刻,只能是其中 一種型別,但這不是固定的。不同的時刻,它可能擁有不同的型別,一個 Server
程式的型別是如何改變的,後面會有解釋。
在一個由 Raft
協議組織的叢集中有三類角色:
- Leader(領袖)
- Follower(群眾)
- 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
節點 複製資料 並 等待接收響應。 -
叢集中至少 超過半數 的節點 已接收 到資料後,
Leader
再向Client
確認資料 已接收。 -
一旦向
Client
發出資料接收Ack
響應後,表明此時 資料狀態 進入 已提交(Committed
),Leader
節點再向Follower
節點發通知告知該 資料狀態已提交。
在這個過程中,主節點 可能在 任意階段 掛掉,看下 Raft
協議如何針對不同階段保障 資料一致性 的。
1. 情形1
資料到達 Leader 節點前,這個階段 Leader 掛掉不影響一致性,不用多說。
2. 情形2
資料到達 Leader 節點,但未複製到 Follower 節點。
這個階段 Leader
掛掉,資料屬於 未提交狀態,Client
不會收到 Ack
會認為 超時失敗 可安全發起 重試。
Follower
節點上沒有該資料,重新選主 後 Client
重試 重新提交 可成功。原來的 Leader
節點 恢復 後作為 Follower
加入叢集,重新從 當前任期 的新 Leader
處 同步資料,強制保持和 Leader
資料一致。
3. 情形3
資料到達 Leader 節點,成功複製到 Follower 所有節點,但 Follower 還未向 Leader 響應接收。
這個階段 Leader
掛掉,雖然資料在 Follower
節點處於 未提交狀態(Uncommitted
),但是 保持一致 的。重新選出 Leader
後可完成 資料提交。
此時 Client
由於不知到底提交成功沒有,可重試提交。針對這種情況 Raft
要求 RPC
請求實現 冪等性,也就是要實現 內部去重機制。
4. 情形4
資料到達 Leader 節點,成功複製到 Follower 的部分節點,但這部分 Follower 節點還未向 Leader 響應接收。
這個階段 Leader
掛掉,資料在 Follower
節點處於 未提交狀態(Uncommitted
)且 不一致。
Raft
協議要求投票只能投給擁有 最新資料 的節點。所以擁有最新資料的節點會被選為 Leader
,然後再 強制同步資料 到其他 Follower
,保證 資料不會丟失並 最終一致。
5. 情形5
資料到達 Leader 節點,成功複製到 Follower 所有或多數節點,資料在 Leader 處於已提交狀態,但在 Follower 處於未提交狀態。
這個階段 Leader
掛掉,重新選出 新的 Leader
後的處理流程和階段 3
一樣。
6. 情形6
資料到達 Leader 節點,成功複製到 Follower 所有或多數節點,資料在所有節點都處於已提交狀態,但還未響應 Client。
這個階段 Leader
掛掉,叢集內部資料其實已經是 一致的,Client
重複重試基於冪等策略對 一致性無影響。
7. 情形7
網路分割槽導致的腦裂情況,出現雙 Leader 的現象。
網路分割槽 將原先的 Leader
節點和 Follower
節點分隔開,Follower
收不到 Leader
的 心跳 將 重新 發起選舉產生新的 Leader
,這時就產生了 雙Leader 現象。
原先的 Leader
獨自在一個區,向它提交資料不可能複製到多數節點所以永遠提交不成功。向新的 Leader
提交資料可以提交成功。
網路恢復 後,舊的 Leader
發現叢集中有 更新任期(Term
)的新 Leader
,則 自動降級 為 Follower
並從新 Leader
處 同步資料 達成叢集 資料一致。
驗證結果
綜上窮舉分析了 最小叢集(3
節點)面臨的所有情況,可以看出 Raft
協議都能很好的應對 一致性問題,並且很容易理解。
小結
Paxos
演算法是 Leslie Lamport
在 1990
年就公開發表在了自己的網站上,想想我們是什麼時候才聽說的?什麼時候才有一個可用的實現?而 Raft
演算法是 2013
年發表的,大家在參考 Raft開源實現庫,可以看到有很多基於不同語言的 開源實現庫,這就是 可理解性 的重要性。
相關連結
- 分散式理論(一) - CAP定理
- 分散式理論(二) - BASE理論
- 分散式理論(三) - 2PC協議
- 分散式理論(四) - 3PC協議
- 分散式理論(五) - 一致性演算法Paxos
- 分散式理論(六) - 一致性協議Raft
歡迎關注公眾號: 零壹技術棧
本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。