1. 程式人生 > 實用技巧 >淺談分散式共識演算法raft

淺談分散式共識演算法raft

前言:在分散式的系統中,存在很多的節點,節點之間如何進行協作執行、高效流轉、主節點掛了怎麼辦、如何選主、各節點之間如何保持一致,這都是不可不面對的問題,此時raft演算法應運而生,專門 用來解決上述問題。對於分散式的一致性演算法,著名的有paxos,zookeeper基於paxos提出了zab協議, paxos是出名的晦澀難懂.而raft的設計初衷就是容易理解和簡單、高效,本篇部落格我們就來循序漸進的看看raft到底是什麼?它的執行原理是什麼樣的?

本篇部落格的目錄:

一:raft的狀態

二:選主過程

三:如何保證叢集一致性

四:如何處理腦裂問題

五:總結

一:raft的狀態

raft的叢集角色分為3種,不同的節點在執行環境中處於不同的角色,任何節點的任何一個時刻都處於以下三種角色之一,不同的角色具有不同的功能,所承擔的職責也不一樣:

①:follower

follwer是叢集的初始狀態,所有的節點在剛開始加入到叢集中,預設是follower的角色,也就是從節點~

②:candidate

candidate的含義可以理解為候選人,這是專門用於在follower在進行選舉的時候,被投票者的稱謂,這是一箇中間角色,followerA會發起投票到followerB,此時followerA的角色就是candidate

③:leader

有了從節點之後就必須有主節點了,所以接下來伴隨的是選主的過程,選主之後的節點稱之為leader,也就是主節點,主節點只有一個,由leader接收使用者的請求,每一次請求都被被記錄到log entry中

三個角色可以這樣理解:試想新學期開學第一天,所有的同學都是普通學生的身份(follower),新學期開始需要挑選出班長(leader),需要進行投票,大家先選出幾個候選人,候選人可以自己投票給自己,此時要選中成為班長的這幾個人就是candidate,最後經過選舉出來的班長就是leader

任期:

任期簡稱Term,是raft裡面非常重要的概念,每個任期可以是任意時長,任期用連續的整數進行標號,每個節點都維護著當前的任期值,每個節點在檢測到自己的任期值低於其他節點會更新自己的任期值,設定為檢測到的較高值。當leader和candidate發現自己的任期低於別的節點,則會立即把自己轉換為follower

下面是幾種角色的流轉圖:

二:raft的選主

2.1:leader負責處理客戶端的請求

所有對日誌的新增或者狀態變化的操作都是通過leader來完成,當leader接收請求之後會將日誌分發到叢集的所有follower節點,日誌的資料流是從leader到其他的節點,而不會產生follower流向日誌的情況,raft會保證流向所有follower節點的日誌副本都是一致的:

選舉leader發生在以下兩種情況:

①當一個raft叢集初始化的時候②當選舉出來的主節點宕機、崩潰的時候

2.2:接下來談一談raft是如何選主的:

一個Raft叢集開始,叢集中的節點所有的初始狀態都是 Follower,然後設定任期(term為0),並啟動計時器發起選舉(Election),開始選舉之後每個參與方都會有一個隨機的超時時間(Election Timeout),這裡的關鍵就是隨機 Timeout(150ms 到 300ms之間,最先走完timeout時間的一個節點開始發起投票,向還在 timeout 中的另外節點請求投票(Reuest Vote)並等待回覆,此時它就只能投給自己,然後raft會統計得票數,在計數器時間內,得票最多的會成為leader.這樣的結果就是最先發起投票的節點會有大概率成為主節點,選出 Leader 後,term值會+1,並且Leader通過定期向所有 Follower 傳送心跳資訊(官方稱之為:Append Entries,Append Entries是一種RPC協議)保持連線。

兩個節點同時發起選舉

因為follwer節點的超時時間是隨機的,所以可能會存在兩個節點正好隨機到相同的random time,並且擁有相同的term,此時raft會如何處理呢?raft會在相同的random time out時間同時發起leader選舉,因為兩個Candidate存在相同的term和timeout,並且同時發起投票,最終他們得到的votes是相同的。這個時候raft會等待下一輪的重試,下一輪兩個節點的time out可能會不同,重試直到選舉出leader

2.3:當選舉完成之後考慮以下幾個情形:

情形一:leader宕機

每次當leader對所有的followe發出Append Entries的時候,follower會有一個隨機的超時時間,如果再超時時間內收到了leader的請求就會重置超時時間,如果沒有收到超過超時時間,follower沒有收到Leader的心跳,follower會認為Leader可能已經掛了,此時第一個超時的follower會發起投票,注意這個時候它依然會向宕機的原leader發出Reuest Vote,但原leader不會回覆。raft設計的

請求投票都是冪等的,會檢測狀態。當收到叢集超過一半的節點的RequestVote reply後,此時的follower會成為leader

ps:後期leader恢復正常之後,加入到raft叢集,初始化的角色是follower,而並非leader。因為任何時刻leader只有一個,如果是兩個,就會發生"腦裂"問題

情形二:follower宕機

follower宕機對整個叢集影響不大,最多的影響是leader發出的Append Entries無法被收到,但是leader還會繼續一直髮送,直到follower恢復正常。raft會保證傳送AppendEntries request的rpc訊息是冪等的,如果follower已經接受到了訊息,但是leader又讓它再次接受,follower會直接忽略

三:raft如何保證叢集的一致性

3.1:Raft 協議由leader節點負責接收客戶端的請求,leader會將請求包裝成log entry分發到從節點,所以叢集強依賴 Leader節點的可用性,以確保叢集 資料的一致性。資料的流向只能從 Leader 節點向 Follower 節點轉移,這個過程叫做日誌複製(Log Replication)

① 當 Client 向叢集 Leader 節點 提交資料 後,Leader 節點 接收到的資料 處於 未提交狀態(Uncommitted)。

接著 Leader 節點會併發地向所有Follower節點複製資料並等待接收響應ACK

③ leader會等待叢集中至少超過一半的節點已接收到資料後, Leader 再向 Client 確認資料 已接收。

④ 一旦向 Client 發出資料接收 Ack 響應後,表明此時 資料狀態 進入 已提交(Committed),Leader 節點再向 Follower 節點發通知告知該資料狀態已提交

⑤ follower開始commit自己的資料,此時raft叢集達到主節點和從節點的一致

3.2:在進行一致性複製的過程中,假如出現了異常情況,raft都是如何處理的呢?

1.資料到達 Leader 節點前,這個階段 Leader 掛掉不影響一致性

2.資料到達 Leader 節點,但未複製到 Follower 節點。這個階段Leader掛掉,資料屬於未提交狀態,Client不會收到Ack會認為超時失敗可安全發起重試。

3.資料到達 Leader 節點,成功複製到 Follower 所有節點,但 Follower 還未向 Leader 響應接收。這個階段Leader掛掉,雖然資料在Follower節點處於未提交狀態(Uncommitted),但是保持一致的。重新選出Leader後可完成資料提交。

4.資料到達 Leader 節點,成功複製到 Follower 的部分節點,但這部分 Follower 節點還未向 Leader 響應接收。這個階段Leader掛掉,資料在Follower節點處於未提交狀態(Uncommitted)且不一致。

Raft協議要求投票只能投給擁有最新資料的節點。所以擁有最新資料的節點會被選為Leader,然後再強制同步資料到其他Follower,保證資料不會丟失並最終一致。 5.資料到達 Leader 節點,成功複製到 Follower 所有或多數節點,資料在 Leader 處於已提交狀態,但在 Follower 處於未提交狀態。

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

6.資料到達 Leader 節點,成功複製到 Follower 所有或多數節點,資料在所有節點都處於已提交狀態,但還未響應 Client。這個階段Leader掛掉,叢集內部資料其實已經是一致的,Client重複重試基於冪等策略對一致性無影響。

四:如何解決腦裂問題

當raft在叢集中遇見網路分割槽的時候,叢集就會因此而相隔開,在不同的網路分割槽裡會因為無法接收到原來的leader發出的心跳而超時選主,這樣就會造成多leader現象,見下圖:在網路分割槽1和網路分割槽2中,出現了兩個leaderA和D,假設此時要更新分割槽2的值,因為分割槽2無法得到叢集中的大多數節點的ACK,會複製失敗。而網路分割槽1會成功,因為分割槽1中的節點更多,leaderA能得到大多數迴應

當網路恢復的時候,叢集不再是雙分割槽,raft會有如下操作:

①: leaderD發現自己的Term小於LeaderA,會自動下臺(step down)成為follower,leaderA保持不變依舊是叢集中的主leader角色

②: 分割槽中的所有節點會回滾roll back自己的資料日誌,並匹配新leader的log日誌,然後實現同步提交更新自身的值。通知舊leaderA也會主動匹配主leader節點的最新值,並加入到follower中

③: 最終叢集達到整體一致,叢集存在唯一leader(節點A)

五:總結

本篇部落格從整體上講了下raft的狀態角色、如何選舉出leader、如何保證一致性、以及如何處理網路分割槽時的腦裂問題,整理較為粗略,raft實現起來更為複雜和細緻,所以這裡只是淺談一下。理解raft的主要目的在於分散式環境中,對於叢集之間的節點互動、宕機後如何處理如何保證高可用、高一致性有一定的理解。