前言:在分佈式的系統中,存在不少的節點,節點之間如何進行協做運行、高效流轉、主節點掛了怎麼辦、如何選主、各節點之間如何保持一致,這都是不可不面對的問題,此時raft算法應運而生,專門 用來解決上述問題。對於分佈式的一致性算法,著名的有paxos,zookeeper基於paxos提出了zab協議, paxos是出名的晦澀難懂.而raft的設計初衷就是容易理解和簡單、高效,本篇博客咱們就來按部就班的看看raft究竟是什麼?它的運行原理是什麼樣的?算法
本篇博客的目錄:安全
raft的集羣角色分爲3種,不一樣的節點在運行環境中處於不一樣的角色,任何節點的任何一個時刻都處於如下三種角色之一,不一樣的角色具備不一樣的功能,所承擔的職責也不同:網絡
follwer是集羣的初始狀態,全部的節點在剛開始加入到集羣中,默認是follower的角色,也就是從節點~併發
candidate的含義能夠理解爲候選人,這是專門用於在follower在進行選舉的時候,被投票者的稱謂,這是一箇中間角色,followerA會發起投票到followerB,此時followerA的角色就是candidatedom
有了從節點以後就必須有主節點了,因此接下來伴隨的是選主的過程,選主以後的節點稱之爲leader,也就是主節點,主節點只有一個,由leader接收用戶的請求,每一次請求都被被記錄到log entry中分佈式
三個角色能夠這樣理解:試想新學期開學第一天,全部的同窗都是普通學生的身份(follower),新學期開始須要挑選出班長(leader),須要進行投票,你們先選出幾個候選人,候選人能夠本身投票給本身,此時要選中成爲班長的這幾我的就是candidate,最後通過選舉出來的班長就是leaderspa
任期簡稱Term,是raft裏面很是重要的概念,每一個任期能夠是任意時長,任期用連續的整數進行標號,每一個節點都維護着當前的任期值,每一個節點在檢測到本身的任期值低於其餘節點會更新本身的任期值,設置爲檢測到的較高值。當leader和candidate發現本身的任期低於別的節點,則會當即把本身轉換爲follower設計
下面是幾種角色的流轉圖:日誌
全部對日誌的添加或者狀態變化的操做都是經過leader來完成,當leader接收請求以後會將日誌分發到集羣的全部follower節點,日誌的數據流是從leader到其餘的節點,而不會產生follower流向日誌的狀況,raft會保證流向全部follower節點的日誌副本都是一致的:code
選舉leader發生在如下兩種狀況:
①當一個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
每次當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宕機對整個集羣影響不大,最多的影響是leader發出的Append Entries沒法被收到,可是leader還會繼續一直髮送,直到follower恢復正常。raft會保證發送AppendEntries request的rpc消息是冪等的,若是follower已經接受到了消息,可是leader又讓它再次接受,follower會直接忽略
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集羣達到主節點和從節點的一致
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
,保證 數據不會丟失並 最終一致。
這個階段 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的主要目的在於分佈式環境中,對於集羣之間的節點交互、宕機後如何處理如何保證高可用、高一致性有必定的理解。