看動畫輕鬆學會 Raft 算法

因爲 Paxos 算法過於晦澀難懂且難以實現,Diego Ongaro 提出了一種更易於理解和實現並能等價於 Paxos 算法的共識算法 - Raft 算法。html

由於 Raft 算法清晰易懂愈來愈多的開源項目嘗試引入 Raft 算法來解決分佈式一致性問題。在分佈式存儲領域基於 Raft 算法構建的項目百花齊放,欣欣向榮。python

介紹 Raft 算法的文章早已經是汗牛充棟,本文先介紹兩個很是優秀的網站:git

The Secret Lives of Data-CN 以圖文方式介紹 Raft 算法,是很是好的入門材料。將其閱讀完後您大機率已經瞭解了 Raft 算法,若是您仍有疑問能夠回來繼續閱讀本文。github

既然您已經回來繼續閱讀,相信您已經瞭解 Raft 算法中的Leader 選舉、日誌複製等基本概念, 但仍有部分疑惑。不要緊, 接下來咱們會解決這些問題。算法

Raft Scope 是 Raft 官方提供的互動式演示程序,它展現了 Raft 集羣的工做狀態。您能夠用它模擬節點宕機、心跳超時等各類狀況。有了 Raft Scope 咱們能夠親自「動手」 觀察 Raft 集羣是如何工做、如何處理各類故障的。安全

遺憾的是這個程序幾乎沒有任何說明很是難以上手。本文接下來將先介紹如何使用 Raft Scope 而後用它模擬幾種 Raft 集羣工做中會遭遇的典型情況。網絡

Raft Scope 說明

能夠看到 Raft Scope 界面由三部分組成。併發

最下方有兩個滑塊:上面的是進度條您能夠拖動它回看剛剛發生過事件,下面的是變速器滑塊越靠左系統運行越慢。分佈式

左上角部分是一個由 5 個節點組成的 Raft 集羣,每一個圓圈表明集羣中的一個節點。點擊節點能夠看到它的狀態。對話框的右下角有一些按鈕,咱們能夠點擊按鈕模擬各類情況。咱們直接右鍵點擊節點也能夠看到這些按鈕網站

這些按鈕的功能是:

  • stop: 節點停機
  • resume: 啓動停機的節點
  • restart: 將節點當即重啓
  • time out: 模擬心跳超時,點擊按鈕後相應節點會認爲 Leader 發生了心跳超時。
  • request: 向集羣提交新的數據

節點中間的數字是節點當前的任期號(Term), 節點的顏色彷佛一樣是用來表示任期的。
節點可能處於 Follower、Candidate 或者 Leader 狀態。

S2 處於 Candidate 狀態,實心原點表示它如今收到的投票。圖中的兩個原點表示收到了 S2 和 S4 的投票,這 5 個小圓點和集羣中節點的位置是對應的,左下角的小圓點表示 S4, 最上面的小圓點表示 S1。在集羣選舉過程當中節點外的動態邊框表示 Election Timeout。

黑色實心邊框表示 S5 是 Leader。Follower 外面的邊框表示 HeartBeat 超時倒計時。

右上角的表格表示各節點的日誌,每行表示一個節點。

表格最上面的數字是日誌的序號(Log Index)。Log Index 是一個自增且連續的 ID,它能夠做爲一條日誌惟一標識。節點中最大的 Log Index 也反映了這個節點的狀態機是否與集羣一致。

表格裏的單元格表示日誌項(Entry),其中的數字表示提交日誌的任期(Term)。虛線框表示日誌還沒有提交,實線框表示日誌已經提交。

咱們能夠點擊 leader 節點的 request 按鈕來查看向 Raft 集羣提交數據的過程。

Leader 選舉

Raft Scope 啓動後會當即進行第一次 Leader 選舉,在集羣運行過程任何一個 Follower 出現心跳超時都會引起新一輪選舉。

咱們能夠點擊任意一個 Follower 的 time out 按鈕模擬心跳超時,隨後此 Follower 會發起新一輪選舉。

或者咱們能夠點擊 Leader 的 stop、restart 來模擬 Leader 宕機或者重啓,並觀察隨後的集羣選舉過程。

比較奇怪的是, Raft Scope 中的 Leader 節點也能夠經過點擊 time out 來模擬心跳超時,在實際的 Raft 集羣中 Leader 節點一般不會對本身進行心跳檢測。

Leader 選舉的更多介紹能夠查看:Leader選舉。不過 The Secret Lives of Data 有兩處說的可能不太清楚:

這裏的選舉超時是指新一輪選舉開始時,每一個節點隨機思考要不要競選 Leader 的時間,這個時間通常100-到200ms,很是短。

Candidate 發起選舉時會將自身任期(Term)+1並向其它全部節點發出 RequestVote 消息,這條消息中包含新任期和 Candidate 節點的最新 Log Index

收到 RequestVote 的節點會進行判斷:

def onRequestVote(self, request_vote)
    if request_vote.term <= self.term:
        # 若 RequestVote 中的任期小於或等於(<=)當前任期
        # 則繼續 Follow 當前 Leader 並拒絕給 RequestVote投票
        return False
    if request_vote.log_index < self.log_index:
        # 若 request_vote 發送者的 log_index 不如本身新,節點也會拒絕給發送者投票
        # 這種機制確保了已經提交到集羣中的日誌不會丟失,即保證 Raft 算法的安全性
        return False
    if self.voted_for is None:
        # 若在本 term 中當前節點還未投票,則給 request_vote 的發送者投票
        self.voted_for = request_vote.sender
        return True
    else:
        return False

Follower 超時

如今咱們研究一下 The Secret Lives of Data 沒有詳細說明的 Follower 超時處理過程。

咱們能夠點擊任意一個 Follower 的 time out 按鈕模擬心跳超時,隨後此 Follower 會發起新一輪選舉。

根據上文中的 onRequestVote 邏輯,超時的 Follower 的 Log Index 是否與集羣中的大多數節點相同決定了此次選舉的不一樣結果。

首先來看超時 Follower 的 Log Index 與集羣中大多數相同的狀況:

如今咱們點擊 S5 的 time out 按鈕,隨後咱們看到 S5 發起了一輪投票。由於 5 個節點的 Log Index 是一致的, 因此包含原 Leader 在內的大多數節點都投票給了 S5。

如今 S5 成爲了新一任 Leader.

接下來咱們看另一種狀況。S5 因爲網絡問題沒有收到帶有 Log Entry 1 的心跳包並致使心跳超時,S5 隨後會發起一次投票:

因爲 S5 的 Log Index 比較小其它節點拒絕投票給他,集羣 Leader 和任期不變:

日誌複製

日誌複製的介紹您能夠查看:日誌複製

如今咱們進一步探究日誌複製的過程:

  1. 客戶端將更改提交給 Leader, Leader 會在本身的日誌中寫入一條未提交的記錄(Entry)
  2. 在下一次心跳時 Leader 會將更改發送給全部 Follower
  3. 一旦收到過半節點的確認 Leader 就會提交本身日誌中的記錄4
  4. 並向客戶端返回寫入成功
  5. Leader 會在下一次心跳時通知全部節點提交日誌

這裏比較複雜的狀況是在第 4 步完成以後 Leader 崩潰。因爲此時客戶端已經收到了寫入成功的回覆,因此在選出新的 Leader 以後要繼續完成提交。

在 Leader 提交了本身的日誌後咱們當即關掉 Leader:

隨後集羣發起了一次選舉,S3 成爲新任 Leader:

多是由於 Raft Scope 存在 Bug, S3 本應該當選後當即完成提交工做。可是實際上須要咱們再一次 Request 以後,日誌1 和日誌 2 纔會被一塊兒提交。

腦裂問題

在 Leader 崩潰時可能會有多個節點近乎同時發現心跳超時並轉變爲 Candidate 開始選舉:

其它節點投票狀況多種多樣,但只要保證獲只有獲得過半投票的候選人才能成爲 Leader。那麼選舉結果只有兩種可能:

  • 有且只有一個候選人得到過半投票成爲 Leader 並開始新的任期
  • 沒有一個候選人得到過半投票,沒有選出 Leader 進入下一輪投票

絕對不會選出多個 Leader

網絡分區問題

Raft 甚至能夠在網絡分區的狀況下正常工做:

在發生網絡分區後可能存在 3 種狀況:

  1. 任意分區中的節點數都不超過一半:這種狀況只有集羣被分紅 3 個或更多分區時纔會出現,十分罕見。由於 Leader 選舉和 Commit Log 都須要超過一半節點確認才能夠進行,在這種狀況下 Raft 集羣不能正常工做。

  2. leader 所在的分區有超過一半的節點:這種狀況視做其它分區中的 Follower 宕機,系統仍然能夠繼續工做。在分區修復後,Follower 節點會從新與 Leader 同步。

  3. leader 所在分區中節點數不超過一半,但存在節點數超過一半的分區。這種狀況最爲複雜:

C、D、E 所在的分區節點數超過一半且與原來的 Leader 沒法通訊,隨後 C、D、E 在心跳超時後會發起新一輪投票選出新的 Leader 並恢復工做。

原領導者 Node B 仍然會認爲本身是集羣的 Leader,可是因爲只能與兩個節點通訊(包括本身)沒法獲得過半節點贊成,因此沒法完成日誌提交。

在分區修復後 Node B 會收到 Node C 的心跳並發現對方的任期(Term)比本身高,Node B 會放棄 Leader 身份轉爲 Node C 的 Follower 與它保持同步。

總結

通過本文探討咱們能夠總結一下 Raft 的一些特性:

  • 只要集羣中有超過一半的節點能夠正常工做,集羣就能夠工做
  • 只要寫入成功的數據就不會再丟失
  • 任意節點上保存的狀態可能會落後於集羣共識可是永遠不會出現錯誤的提交。只要系統仍然在正常工做,節點上的狀態必定會在某個時間後與系統共識達成同步,即保證最終一致性
  • 只要在某個節點上讀到了某個變動, 在此以後這個節點上永遠能夠讀到該變動,即保證單調一致性

推薦閱讀:

相關文章
相關標籤/搜索