摘取自: http://mp.weixin.qq.com/s?__biz=MzIyMTQ1NDE0MQ==&mid=2247483979&idx=1&sn=12864382e233fe9b900ab14349404032&chksm=e83dc819df4a410f5959b6922025d317d6c497b7110c4c5d8720fb2b0a70246ce651f9a19e91&mpshare=1&scene=1&srcid=0125HVXp5VF5Kuav7ko8iCrb#rd網絡
0 - Raft協議和Paxos的因緣分佈式
讀過Raft論文《In Search of an Understandable Consensus Algorithm》的同窗都知道,Raft是由於Paxos而產生的。Paxos協議是出了名的難懂,並且不夠詳細,牢牢依據Paxos這篇論文開發出可用的系統是很是困難的。Raft的做者也說是被Paxos苦虐了無數個回合後,才設計出了Raft協議。做者的目標是設計一個足夠詳細而且簡單易懂的「Paxos協議」,讓開發人員能夠在很短的時間內開發出一個可用的系統。
ui
Raft協議在功能上是徹底等同於(Multi)-Paxos協議的。Raft也是一個原子廣播協議(原子廣播協議參見《由淺入深理解Paxos協議(1)》),它在分佈式系統中的功能以及使用方法和Paxos是徹底同樣的。咱們能夠用Raft來替代分佈式系統中的Paxos協議以下圖所示:設計
1 - Raft的設計理念3d
嚴格來講Raft並不屬於Paxos的一個變種。Raft協議並非對Paxos的改進,也沒有使用Paxos的基礎協議(The Basic Protocol)。Raft協議在設計理念上和Paxos協議是徹底相反的。正是因爲這個徹底不一樣的理念,使得Raft協議變得簡單起來。日誌
Paxos協議中有一個基本的假設前提:可能會同時有多個Leader存在。這裏把Paxos協議執行的過程分爲如下兩個部分:隊列
Leader選舉圖片
數據廣播ci
在《由淺入深理解Paxos協議(2)》的「Leader的選取」一節中提到過,Paxos協議並無給出詳細的Leader選舉機制。Paxos對於Leader的選舉沒有限制,用戶能夠本身定義。這是由於Paxos協議設計了一個巧妙的數據廣播過程,即Paxos的基本通信協議(The Basic Protocol)。它有很強的數據一致性保障,即便在多個Leader同時出現時也可以保證廣播數據的一致性。開發
而Raft協議走了徹底相反的一個思路:保證不會同時有多個Leader存在。所以Raft協議對Leader的選舉作了詳細的設計,從而保證不會有多個Leader同時存在。相反,數據廣播的過程則變的簡單易於理解了。
2 - Raft的日誌廣播過程
爲了保證數據被複制到多數的節點上,Raft的廣播過程儘管簡單仍然要使用多數派協議,只是這個過程要容易理解的多:
發送日誌到全部Followers(Raft中將非Leader節點稱爲Follower)。
Followers收到日誌後,應答收到日誌。
當半數以上的Followers應答後,Leader通知Followers日誌廣播成功。
- 日誌和日誌隊列
Raft將用戶數據稱做日誌(Log),存儲在一個日誌隊列裏。每一個節點上都有一份。隊列裏的每一個日誌都一個序號,這個序號是連續遞增的不能有缺。
日誌隊列裏有一個重要的位置叫作提交日誌的位置(Commit Index)。將日誌隊列裏的日誌分爲了兩個部分:
已提交日誌:已經複製到超過半數節點的數據。這些日誌是能夠發送給應用程序去執行的日誌。
未提交日誌:還未複製到超過半數節點的數據。
當Followers收到日誌後,將日誌按順序存儲到隊列裏。但這時Commit Index不會更新,所以這些日誌是未提交的日誌,不能發送給應用去執行。當Leader收到超過半數的Followers的應答後,會更新本身的Commit Index,並將Commit Index廣播到Followers上。這時Followers更新Commit Index,未提交的日誌就變成了已提交的日誌,能夠發送給應用程序去執行了。
從上面的解釋咱們能夠知道,日誌隊列中已經提交的日誌是不可改變的,而未提交的日誌則能夠被更新成其餘的日誌(在Leader發生變化時會發生)。
Raft的日誌隊列和《由淺入深理解Paxos協議(1)》中的「預存儲隊列+存儲隊列」功能是同樣的,可是巧妙的合併到了一塊兒。這樣作解決的問題和《由淺入深理解Paxos協議(1)》中「預存儲隊列+存儲隊列」解決的問題也是同樣的,這裏就再也不敘述。
3 - Raft的Leader選舉
Raft稱它的Leader爲「Strong Leader」。Strong Leader 有如下特色:
同一時間只有一個Leader
只能從Leader向Followers發送數據,反之不行。
下面咱們看一下Raft經過哪些機制來實現Strong Leader。
- 多數派協議
爲了保證只有一個Leader被選舉出來,選舉的過程使用了多數派協議。這樣很好理解,當一個Candidate(申請成爲Leader的節點)請求成爲Leader時,只有半數以上的Followers贊成後,才能成爲Leader。投票過程以下:
當發現Leader無響應後(一段時間內沒有日誌或心跳),Candidate發送投票請求。
Followers投票。
若是超過半數的Followers投了票,則Candidate自動變成Leader,開始廣播日誌。
- 隨機超時機制
和《由淺入深理解Paxos協議(1)》中提到問題同樣,這裏也會發生多個Candidate同時發送投票請求,而致使誰都不可以獲得多數同意票的狀況,有可能永遠也選不出Leader。爲了保證Leader選舉的效率,Raft在投票選舉中使用了隨機超時的機制:
在每一個Followers上設定的Leader超時時間是在一個範圍內隨機的。這樣能夠儘可能讓Followers不在同一時間發起Leader選舉。
每一個Candidate發起投票後,若是在一段時間內沒有任何Candidate稱爲Leader則,須要從新發起Leader選舉。這段等待的時間,在每一個Candidate上也是隨機的。從而保證不會有多個Candidate同時從新發起Leader選舉。
雖然說是隨機的超時時間,可是也有個範圍,過小或者太大都會影響系統的可用性。過小會致使過多的選舉衝突,太大又會影響系統的平滑運行。在Raft的論文中,做者將這個超時時間稱爲electionTimeout,並給出了合理的範圍,公式以下:
broadcastTime ≪ electionTimeout ≪ MTBF
「≪」表明數量級上的差別(10倍以上)。
- Candidate的日誌長度要等於或者超過半數節點才能選爲Leader
當Leader故障時,Followers上日誌的狀態極可能是不一致的。有的多有的少,並且Commit Index也不盡相同。
咱們知道已經提交的日誌是不可以丟棄的,必需要最終複製到全部的節點上才行。假如在選Leader時,圖中Candidate A變成了Leader,就必需要首先從Candidate B上將日誌4複製過來,而後才能開始處理新的日誌。爲了減小複雜性,raft就規定,只有包含了全部已提交日誌的Candidate才能當選爲Leader。
實現也很簡單:
當發現Leader無響應後(一段時間內沒有數據或心跳),Candidate發送投票請求,請求中包含本身日誌隊列的長度(或者說最大日誌的Index)。
Followers檢查Candidate的日誌長度,只有Candidate的日誌等於或者長於本身才投票。
若是超過半數的Followers投了票,則Candidate自動變成Leader,開始廣播數據。
由於已經提交的日誌必定被複制到了多數節點上,因此日誌長度等於或者長於多數節點的Candidate必定包含了全部已經提交的日誌。
爲何不是檢查Commit Index?
由於Leader故障時,頗有可能只有Leader的Commit Index是最大的。
若是圖中的Candidate A被選舉爲Leader,那麼日誌4就會被丟棄。可是日誌4已經在原來的Leader上提交了,所以必須被保留才行。因此只能讓日誌長度更長的Candidate B選爲Leader。這種作法有可能把原來Leader沒廣播完成的日誌(圖中的日誌5)接着廣播完成,這沒有什麼關係。
- Followers日誌補齊
當Leader故障時,Followers上的日誌狀態是不同的,有長有短。所以新的Leader選出後,首先要將全部Followers的日誌補齊才行。所以Leader要詢問Followers的日誌長度,從最小的日誌位置開始補齊。
- Followers未提交日誌的更新
新Leader的日誌必定包含全部已經提交的日誌。但新Leader的日誌不必定是最長的,那些新Leader沒有的日誌,必定是未提交的日誌,所以能夠被更新,沒有關係的。Leader只須要從本身的當前位置開始插入日誌並廣播出去就能夠了。Followers會用新的日誌去更新指定位置上的日誌。
4 - 新舊Leader的交替
新的Leader選出後,開始廣播日誌。這時若是舊的Leader故障恢復了(好比網絡臨時中斷),而且還認爲本身是Leader,也會廣播日誌。這不就致使了同時有兩個Leader出現嗎?是的,Raft也沒辦法讓舊的Leader不發日誌,可是Raft有辦法讓Followers拒絕舊Leader的日誌。
- Term
Raft將時間劃分爲連續的時間段,稱爲Term。 Term是指從一次Leader選舉開始到下一次Leader選舉的一段時間。這段時間內只能有一個Leader被選舉成功,並負責管理系統或者沒有Leader選出。
Raft論文上的Term圖片
每一個Term都有一個惟一的數字編號。全部Term的數字編號是從小到大連續排列的。
- 做廢舊Leader
Term編號在做廢舊Leader的過程當中相當重要,但卻十分簡單。過程以下:
發送日誌到全部Followers,Leader的Term編號隨日誌一塊兒發送。
Followers收到日誌後,檢查Leader的Term編號。若是Leader的Term編號等於或者大於本身的當前Term(Current Term)編號,則存儲日誌到隊列而且應答收到日誌。不然發送失敗消息給Leader,消息中包含本身的當前Term編號。
當Leader收到任何Term編號比本身的Term編號大的消息時,則將本身變成Follower。收到的消息包括:Follower給本身的回覆消息、新Leader的日誌廣播消息、Leader的選舉消息。
- Raft的實現
論文中做者僅用了兩個RPC就實現了Raft的功能,它們分別是:
RequestVote() Candidate發起的投票請求
AppendEntries() 將日誌廣播到Followers上
AppendEntries()除了廣播日誌外,做者還巧妙的用它實現瞭如下的功能:
發送心跳(heartbeat): 沒有客戶日誌時,經過AppendEntries()廣播空日誌,當作心跳。
發送Commit Index:當Commit Index更新後,能夠隨着當前的日誌經過AppendEntries()廣播到Followers上。若是沒有客戶端日誌,則能夠隨着心跳廣播出去。