raft 圖解 (秒懂)

raft算法之因此容易理解,其一是他將一致性問題劃分紅幾個子問題,這幾個子問題都是獨立、可理解和解釋的。從傳統的思惟來說,對於一個複雜的系統或者工程,都是大化小,分解實現,而後去嘗試融合解決總體邏輯。mysql

一、Raft 詳解

Raft 算法是分佈式系統開發首選的共識算法。好比如今流行 Etcd、Consul、Nacos。算法

若是掌握了這個算法,就能夠較容易地處理絕大部分場景的容錯一致性需求。好比分佈式配置系統、分佈式 NoSQL 存儲等等,輕鬆突破系統的單機限制。sql

Raft 算法是經過一切以領導者爲準的方式,實現一系列值的共識和各節點日誌的一致。數據庫

1.1 Raft 角色

跟隨者(Follower)普通羣衆,默默接收和來自領導者的消息,當領導者心跳信息超時的時候,就主動站出來,推薦本身當候選人。數組

候選人(Candidate)候選人將向其餘節點請求投票 RPC 消息,通知其餘節點來投票,若是贏得了大多數投票選票,就晉升當領導者。安全

領導者(Leader)霸道總裁,一切以我爲準。處理寫請求、管理日誌複製和不斷地發送心跳信息,通知其餘節點「我是領導者,我還活着,大家不要」發起新的選舉,不用找新領導來替代我。服務器

以下圖所示,分別用三種圖表明跟隨者、候選人和領導者。網絡

角色

1.1 數據庫服務器app

如今咱們想象一下,有一個單節點系統,這個節點做爲數據庫服務器,且存儲了一個值爲 X。分佈式

數據庫服務器

左邊綠色的實心圈就是客戶端,右邊的藍色實心圈就是節點 a(Node a)。Term 表明任期,後面會講到。

客戶端

客戶端向單節點服務器發送了一條更新操做,設置數據庫中存的值爲 8。單機環境下(單個服務器節點),客戶端從服務器拿到的值也是 8。一致性很是容易保證。

客戶端向服務器發送數據

但若是有多個服務器節點,怎麼保證一致性呢?好比有三個節點:a,b,c。以下圖所示。這三個節點組成一個數據庫集羣。客戶端對這三個節點進行更新操做,如何保證三個節點中存的值一致?這個就是分佈式一致性問題。Raft 算法就是來解決這個問題的。固然還有其餘協議也能夠保證,本篇只針對 Raft 算法。

在多節點集羣中,在節點故障、分區錯誤等異常狀況下,Raft 算法如何保證在同一個時間,集羣中只有一個領導者呢?下面就開始講解 Raft 算法選舉領導者的過程。

1.2 初始狀態

初始狀態下,集羣中全部節點都是跟隨者的狀態。

以下圖所示,有三個節點(Node) a、b、c,任期(Term)都爲 0。

mark

Raft 算法實現了隨機超時時間的特性,每一個節點等待領導者節點心跳信息的超時時間間隔是隨機的。好比 A 節點等待超時的時間間隔 150 ms,B 節點 200 ms,C 節點 300 ms。那麼 a 先超時,最早由於沒有等到領導者的心跳信息,發生超時。以下圖所示,三個節點的超時計時器開始運行。

超時時間

1.3 發起投票

當 A 節點的超時時間到了後,A 節點成爲候選者,並增長本身的任期編號,Term 值從 0 更新爲 1,並給本身投了一票。

  • Node A:Term = 1, Vote Count = 1。
  • Node B:Term = 0。
  • Node C:Term = 0。

成爲候選者

1.4 成爲領導者的簡化過程

咱們來看下候選者如何成爲領導者的。

Leader 選舉

  • 第一步:節點 A 成爲候選者後,向其餘節點發送請求投票 RPC 信息,請它們選舉本身爲領導者。
  • 第二步:節點 B 和 節點 C 接收到節點 A 發送的請求投票信息後,在編號爲 1 的這屆任期內,尚未進行過投票,就把選票投給節點 A,並增長本身的任期編號。
  • 第三步:節點 A 收到 3 次投票,獲得了大多數節點的投票,從候選者成爲本屆任期內的新的領導者。
  • 第四步:節點 A 做爲領導者,固定的時間間隔給 節點 B 和節點 C 發送心跳信息,告訴節點 B 和 C,我是領導者,組織其餘跟隨者發起新的選舉。
  • 第五步:節點 B 和節點 C 發送響應信息給節點 A,告訴節點 A 我是正常的。

1.5 領導者的任期

英文單詞是 term,領導者是有任期的。

  • 自動增長:跟隨者在等待領導者心跳信息超時後,推薦本身爲候選人,會增長本身的任期號,如上圖所示,節點 A 任期爲 0,推舉本身爲候選人時,任期編號增長爲 1。

  • 更新爲較大值:當節點發現本身的任期編號比其餘節點小時,會更新到較大的編號值。好比節點 A 的任期爲 1,請求投票,投票消息中包含了節點 A 的任期編號,且編號爲 1,節點 B 收到消息後,會將本身的任期編號更新爲 1。

  • 恢復爲跟隨者:若是一個候選人或者領導者,發現本身的任期編號比其餘節點小,那麼它會當即恢復成跟隨者狀態。這種場景出如今分區錯誤恢復後,任期爲 3 的領導者受到任期編號爲 4 的心跳消息,那麼前者將當即恢復成跟隨者狀態。

  • 拒絕消息:若是一個節點接收到較小的任期編號值的請求,那麼它會直接拒絕這個請求,好比任期編號爲 6 的節點 A,收到任期編號爲 5 的節點 B 的請求投票 RPC 消息,那麼節點 A 會拒絕這個消息。

  • 一個任期內,領導者一直都會領導者,直到自身出現問題(如宕機),或者網絡問題(延遲),其餘節點發起一輪新的選舉。

  • 在一次選舉中,每個服務器節點最多會對一個任期編號投出一張選票,投完了就沒了。

假設一個集羣由 N 個節點組成,那麼大多數就是至少 N/2+1。例如: 3 個節點的集羣,大多數就是 2。

1.6 防止多個節點同時發起投票

爲了防止多個節點同時發起投票,會給每一個節點分配一個隨機的選舉超時時間。這個時間內,節點不能成爲候選者,只能等到超時。好比上述例子,節點 A 先超時,先成爲了候選者。這種巧妙的設計,在大多數狀況下只有一個服務器節點先發起選舉,而不是同時發起選舉,減小了因選票瓜分致使選舉失敗的狀況。

成爲候選者

1.7 觸發新的一輪選舉

若是領導者節點出現故障,則會觸發新的一輪選舉。以下圖所示,領導者節點 B 發生故障,節點 A 和 節點 B 就會從新選舉 Leader。

領導者故障

  • 第一步 :節點 A 發生故障,節點 B 和節點 C 沒有收到領導者節點 A 的心跳信息,等待超時。
  • 第二步:節點 C 先發生超時,節點 C 成爲候選人。
  • 第三步:節點 C 向節點 A 和 節點 B 發起請求投票信息。
  • 第四步:節點 C 響應投票,將票投給了 C,而節點 A 由於發生故障了,沒法響應 C 的投票請求。
  • 第五步:節點 C 收到兩票(大多數票數),成爲領導者。
  • 第六步:節點 C 向節點 A 和 B 發送心跳信息,節點 B 響應心跳信息,節點 A 不響應心跳信息。

1.8 Raft 算法的幾個關鍵機制

Raft 算法經過如下幾個關鍵機制,保證了一個任期只有一位領導,極大減小了選舉失敗的狀況。

  • 任期
  • 領導者心跳信息
  • 隨機選舉超時時間
  • 先來先服務的投票原則
  • 大多數選票原則

2. Raft算法的典型應用

Raft算法的典型應用包括:

  • 領導選舉

  • 日誌複製

對於一個集羣只有一個leader(領導),那麼咱們就很容易理解。只要領導操做同步到對應的followers(跟隨者),數據必然一致。當leader宕機,須要進行領導選舉。

日誌複製其實就是同步操做數據的過程。leader將操做日誌同步到其餘節點。

安全性:如何安全的同步,在不一樣的狀況,咱們都能保證一致性,這也就是安全性須要考慮的問題。

其實就是如此,raft首先假設了領導選舉。而後實現了日誌複製,最後在安全問題上解決上面的漏洞問題。

1.領導選舉

目的:當集羣初始化或者領導gg的時候選出一個新的領導。畢竟一個集羣不能沒有領導,若是沒有,那麼這個集羣就不可用了。

觸發機制:經過心跳。
1.png
這幅圖展示了跟隨者、候選者和領導者之間的狀態轉換關係,下面主要介紹他們的轉換流程。

跟隨者

若是他能持續從領導者或者候選者接收到有效的RPCs,那麼他的狀態就不會變。一直保持跟隨者身份。但若是沒有持續接受,也就是在一段時間沒收到有效的RPCs,那就選舉超時,他會變成候選者。

候選者

要變爲候選者,也就意味着他要開啓一輪新的選舉。那麼跟隨者會增長本身的任期號,轉爲候選者。

成爲候選者後,本身會並行發送一個爲本身投票的RPCs請求 給其餘服務器。

成爲候選者的整個過程也會保持當前狀態,知道知足下面三個條件

  • 他贏得選舉,轉變爲領導制
  • 其餘節點贏了,他轉爲跟隨者
  • 一段時間沒有任何人獲勝。說明選票被瓜分,重複執行。

這裏須要注意的點:

1.對於同一任期號,每一個節點一會投一張票。好比服務器A做爲候選者生成了編號爲5的任期號,那麼若是接收到其餘節點的編號爲5的任期號的投票請求,他會忽略。這個過程遵循的是先來先投的原則。
2.候選者接收到領導者的聲明。會判聲明中RPC的任期號,若是比本身的還小,那麼他還會保持候選者身份,不然轉爲跟隨者。
3.對於上面第三點,重複執行,Raft採用隨機超時選舉時間進行了優化。下降選票瓜分的可能性。

2.Raft日誌複製

直接去理解日誌複製,是很容易的,客戶端的一條指令到達,領導者會爲這條指令建立一條日誌條目,而且並行發送到其餘跟隨者。當日志被安全複製(所謂安全複製後面會有),領導會將日誌應用到狀態機(好比若是是mysql的insert,那麼就是執行insert操做),而後響應客戶端。

2.png

如上圖,每條日誌都會有對應的任期號,和指令。
每一個日誌都會有對應的索引。

raft日誌匹配特性
1.若是在不一樣的日誌中的兩個條目擁有相同的索引和任期號,那麼他們存儲了相同的指令。
2.若是在不一樣的日誌中的兩個條目擁有相同的索引和任期號,那麼他們以前的全部日誌條目也所有相同。

第一點:一個任期只有一個領導人,而且領導人在一個任期中對於同一索引日誌,只會建立一條日誌,是不會改變的,是肯定的。這就保證第一點成立。
第二點:要想所有相同,就要保證跟隨者獲得的日誌是領導者發送的順序附加上去的。領導者在發送新的日誌時,會附加這條日誌以前日誌的索引和任期號。若是跟隨者發現數據匹配,纔會附加上去,不然拒絕。就是一個個狀態保證了日誌的匹配特性。

對於日誌不一致的現象,raft是經過跟隨者強制複製領導者的日誌來保證的。

3.png

如上圖,對於a-f,最終都會和leader同步,也就是說,d會丟棄日誌。f的對應日誌也會被丟棄和覆蓋。

其實就是經過日誌覆蓋解決。可是對於日誌覆蓋,咱們就會想到一個問題,會不會覆蓋已經提交的日誌(日誌對應指令已經返回給客戶端)。那固然不會,若是真有這樣,就會有不一致,或者指令丟失現象。

那麼如何去作覆蓋跟隨者日誌

其實就是跟隨者在append日誌的時候,會進行錯誤校驗。

在候選者成爲領導者的時候,會爲每一個跟隨者初始化一個nextIndex數組,數組的值初始化爲本身最後條日誌+1,其實就是理想化狀態,默認認爲日誌都已經同步成功。可是理想總會由於各類緣由致使不正確,就用上面那張圖。leader會初始化全部nextIndex爲11,可是在同步日誌的過程當中。f節點會出現校驗錯誤的響應。由於f節點10索引對應的日誌和leader10索引對應的日誌不相同(主要是根據任期號判斷)

這裏再強調一下爲何根據任期號就能夠判斷日誌是否一致,就是上面所說的日志匹配原則。

3.Raft算法的安全性

咱們明白瞭如何選舉和日誌複製,可是沒有考慮安全性問題。其實我上慢提到,好比一個宕機好久的跟隨着會被選爲領導者,進行日誌覆蓋操做會有丟失問題。

其實解決這個辦法很簡單,就是在領導選舉的時候,只能讓安全的節點當leader,所謂安全,就是對應節點擁有當前領導者已經提交的全部日誌。Raft就是這麼作的。

Raft中節點在投票的時候,會判斷被投票的候選者對應的日誌是否至少和本身同樣新。若是不是,則不會給該候選者投票。

日誌比較的方法:
1.最後一條日誌的任期號。若是大說明新。若是小,說明不新。若是相等。跳到2
2.判斷索引長度。大的更新。

還有一個問題,就是領導人不能保證一個已經在大多數節點存在的日誌是否已經提交。

4.png

a、b、c、d、e表明不一樣的任期階段

(a)S1是leader。同步任期2的數據給S2

(b)S1宕機,S5當選(S三、S四、S5投票),產生任期3的日誌

(c)S5宕機,S1恢復當選(同步任期2的數據給S3)。

(d)S1宕機,S5當選(由於他的任期日誌比其餘的都新),複製了任期3的全部數據。

假如說在c階段,S1提交了任期2的數據,那麼若是出現d,則會致使任期2數據被覆蓋,丟失。也就是說,S1在任期4時候,不能保證已經在大多數節點存在的日誌(任期2的日誌)是否提交。

因此raft永遠不會經過計算副本數目的方式(大多數存在)去提交一個以前任期內(任期2)的日誌條目。只有領導人當前任期裏的日誌條目經過計算副本數目能夠被提交(e階段)。這樣以前任期的數據也會被提交。

那這裏我理解一點就是,加入S1當選爲leader,如圖c狀態,那麼,若是再也不有新的日誌出現,任期2對應的日誌就不會提交。那麼會致使客戶端對應的任期2請求失敗。

跟隨者和候選人宕機

這個就比較容易理解了,宕機的話RPCs就會失敗,Raft經過無限重試卻解決這個問題。
因此對於每一個RPCs,作到冪等和無限重試,在節點恢復後,就仍是會保證一致性狀態。

Raft集羣成員變化

對於集羣成員配置變化,若是直接更新每臺機器配置,那麼就會有安全性問題。覺得對於同一時刻,不一樣節點使用的不一樣的配置去執行算法邏輯,這就是不安全的。

5.png

如圖,藍色表明新的配置。綠色表明老的配置。Old狀態有三臺機器Server一、二、3。 New加入兩臺server四、5。

那麼隨着配置時間應用的不一樣,可能會致使選舉出兩個leader。
好比server一、2使用老配置,那麼1和2都有可能被當選爲leader。三、四、5使用心得配置,他們之中的一個也會被當選爲leader。

其實這個問題緣由就是節點使用了不一樣配置執行算法邏輯。爲了解決這個問題,raft採用兩階段方法(其實只須要保證不會讓新或者舊配置單獨做出決定就行)

6.png

raft吧配置看成普通日誌形式去提交。

爲了實現兩階段,引入了C(old、new)配置。
還有一點就是,一點一個新的配置日誌增長到對應的節點日誌中,那麼該節點就會馬上使用這條新的日誌配置。

對於C(old、new)配置,其實就是隻有同時知足old和new配置的時候纔會生效。

這樣理想狀態下,若是擁有C(old、new)配置的節點當選爲leader。而且提交了該配置,那麼說明C(old、new)配置已經在大多數節點應用。下次選舉的產生的leader日誌中必然會有該配置。這個時候在建立一條新的C(new)配置提交,便可。

相關文章
相關標籤/搜索