Raft共識算法
拜占庭將軍問題是分佈式領域最複雜、最嚴格的容錯模型。但在平常工做中使用的分佈式系統面對的問題不會那麼複雜,更多的是計算機故障掛掉了,或者網絡通訊問題而無法傳遞信息,這種狀況不考慮計算機之間互相發送惡意信息,極大簡化了系統對容錯的要求,最主要的是達到一致性。html
因此將拜占庭將軍問題根據常見的工做上的問題進行簡化:假設將軍中沒有叛軍,信使的信息可靠但有可能被暗殺的狀況下,將軍們如何達成一致性決定?算法
對於這個簡化後的問題,有許多解決方案,第一個被證實的共識算法是 Paxos,由拜占庭將軍問題的做者 Leslie Lamport 在1990年提出,但由於 Paxos 難懂,難實現,因此斯坦福大學的教授Diego Ongaro 和 John Ousterhout在2014年發表了論文《In Search of an Understandable Consensus Algorithm》,其中提到了新的分佈式協議 Raft
。與 Paxos 相比,Raft 有着基本相同運行效率,可是更容易理解,也更容易被用在系統開發上。babel
Raft實現一致性的機制是這樣的:首先選擇一個leader全權負責管理日誌複製,leader從客戶端接收log entries(日誌條目),將它們複製給集羣中的其它機器,而後負責告訴其它機器何時將日誌應用於它們的狀態機。舉個例子,leader能夠在無需詢問其它server的狀況下決定把新entries放在哪一個位置,數據永遠是從leader流向其它機器(leader的強一致性)。一個leader能夠fail或者與其餘機器失去鏈接,這種情形下會有新的leader被選舉出來。網絡
在任什麼時候刻,每一個server節點有三種狀態:leader
,candidate
,follower
。併發
正常運行時,只有一個leader,其他全是follower。follower是被動的:它們不主動提出請求,只是響應leader和candidate的請求。leader負責處理全部客戶端請求(若是客戶端先鏈接某個follower,該follower要負責把它重定向到leader)。candidate狀態用於選舉領導節點。下圖展現了這些狀態以及它們之間的轉化:分佈式
Raft將時間分解成任意長度的terms
,以下圖所示:動畫
terms有連續單調遞增的編號,每一個term開始於選舉,這一階段每一個candidate都試圖成爲leader。若是一個candidate選舉成功,它就在該term剩餘週期內履行leader職責。在某種情形下,可能出現選票分散,沒有選出leader的狀況,這時新的term當即開始。Raft確保在任何term都只可能存在一個leader。term在Raft用做邏輯時鐘,servers能夠利用term判斷一些過期的信息:好比過期的leader。每臺server都存儲當前term號,它隨時間單調遞增。term號能夠在任何server通訊時改變:若是某個server節點的當前term號小於其它servers,那麼這臺server必須更新它的term號,保持一致;若是一個candidate或者leader發現本身的term過時,則降級成follower;若是某個server節點收到一個過期的請求(擁有過期的term號),它會拒絕該請求。3d
Raft servers使用RPC交互,基本的一致性算法只須要兩種RPC。RequestVote RPCs
由candidate在選舉階段發起。AppendEntries RPCs
在leader複製數據時發起,leader在和follower作心跳時也用該RPC。servers發起一個RPC,若是沒獲得響應,則須要不斷重試。另外,發起RPC是並行的。日誌
raft算法大體能夠劃分爲兩個階段,即Leader Selection
和Log Relocation
,同時使用強一致性來減小須要考慮的狀態。code
Raft使用heartbeat
(心跳機制)來觸發選舉。當server節點啓動時,初始狀態都是follower。每個server都有一個定時器,超時時間爲election timeout
(時間長度通常爲150ms~300ms),若是某server沒有超時的狀況下收到來自leader或者candidate的任何RPC,則定時器重啓,若是超時,它就開始一次選舉。leader給followers發RPC要麼複製日誌,要麼就是用來告訴followers本身是leader,不用選舉的心跳(告訴followers對狀態機應用日誌的消息夾雜在心跳中)。若是某個candidate得到了超過半數節點的選票(本身投了本身),它就贏得了選舉成爲新leader。
上述的具體過程以下:
➢ 初始狀態下集羣中的全部節點都處於 follower 狀態。
➢ 某一時刻,其中的一個 follower 因爲沒有收到 leader 的 heartbeat 率先發生 election timeout 進而發起選舉。
➢ 只要集羣中超過半數的節點接受投票,candidate 節點將成爲即切換 leader 狀態。
➢ 成爲 leader 節點以後,leader 將定時向 follower 節點同步日誌併發送 heartbeat。
若是leader節點出現了故障,那怎麼辦?
下面將說明當集羣中的 leader 節點不可用時,raft 集羣是如何應對的。
➢ 通常狀況下,leader 節點定時發送 heartbeat 到 follower 節點。
➢ 因爲某些異常致使 leader 再也不發送 heartbeat ,或 follower 沒法收到 heartbeat 。
➢ 當某一 follower 發生election timeout 時,其狀態變動爲 candidate,並向其餘 follower 發起投票。
➢ 當超過半數的 follower 接受投票後,這一節點將成爲新的 leader,leader 的任期號term加1並開始向 follower 同步日誌。
➢ 當一段時間以後,若是以前的 leader 再次加入集羣,則兩個 leader 比較彼此的任期號,任期號低的leader將切換本身的狀態爲follower。
➢ 較早前 leader 中不一致的日誌將被清除,並與現有 leader 中的日誌保持一致。
還有第三種可能性就是candidate既沒選舉成功也沒選舉失敗:若是多個follower同時成爲candidate去拉選票,致使選票分散,任何candidate都沒拿到大多數選票,這種狀況下Raft使用超時機制election timeout
來解決。因此同時出現多個candidate的可能性不大,即便機緣巧合同時出現了多個candidate致使選票分散,那麼它們就等待本身的election timeout超時,從新開始一次新選舉,實驗也證實這個機制在選舉過程當中收斂速度很快。
在 raft 集羣中,全部日誌都必須首先提交至 leader 節點。leader 在每一個 heartbeat 向 follower 發送AppendEntries RPC同步日誌,follower若是發現沒問題,複製成功後會給leader一個表示成功的ACK,leader收到超過半數的ACK後應用該日誌,返回客戶端執行結果。若 follower 節點宕機、運行緩慢或者丟包,則 leader 節點會不斷重試AppendEntries RPC,直到全部 follower 節點最終都複製全部日誌條目。
上述的具體過程以下:
➢ 首先有一條 uncommitted 的日誌條目提交至 leader 節點。
➢ 在下一個 heartbeat,leader 將此條目複製給全部的 follower。
➢ 當大多數節點記錄此條目以後,leader 節點認定此條目有效,將此條目設定爲已提交併存儲於本地磁盤。
➢ 在下一個 heartbeat,leader 通知全部 follower 提交這一日誌條目並存儲於各自的磁盤內。
Network Partition 狀況下進行復制日誌:
因爲網絡的隔斷,形成集羣中多數的節點在一段時間內沒法訪問到 leader 節點。按照 raft 共識算法,沒有 leader 的那一組集羣將會經過選舉投票出新的 leader,甚至會在兩個集羣內產生不一致的日誌條目。在集羣從新完整連通以後,原來的 leader 仍會按照 raft 共識算法從步進數更高的 leader 同步日誌並將本身切換爲 follower。
➢ 集羣的理想狀態。
➢ 網絡間隔形成大多數的節點沒法訪問 leader 節點。
➢ 新的日誌條目添加到 leader 中。
➢ leader 節點將此條日誌同步至可以訪問到 leader 的節點。
➢ follower 確認日誌被記錄,可是確認記錄日誌的 follower 數量沒有超過集羣節點的半數,leader 節點並不將此條日誌存檔。
➢ 在被隔斷的這部分節點,在 election timeout 以後,followers 中產生 candidate 併發起選舉。
➢ 多數節點接受投票以後,candidate 成爲 leader。
➢ 一個日誌條目被添加到新的 leader並複製給新 leader 的 follower。
➢ 多數節點確認以後,leader 將日誌條目提交併存儲。
➢ 在下一個 heartbeat,leader 通知 follower 各自提交併保存在本地磁盤。
➢ 通過一段時間以後,集羣從新連通到一塊兒,集羣中出現兩個 leader 而且存在不一致的日誌條目。
➢ 新的 leader 在下一次 heartbeat timeout 時向全部的節點發送一次 heartbeat。
➢ #1 leader 在收到任期號term更高的 #2 leader heartbeat 時放棄 leader 地位並切換到 follower 狀態。
➢ 此時leader同步未被複制的日誌條目給全部的 follower。
經過這種方式,只要集羣中有效鏈接的節點超過總數的一半,集羣將一直以這種規則運行下去並始終確保各個節點中的數據始終一致。
[1] https://www.jianshu.com/p/8e4bbe7e276c
[2] Ongaro D, Ousterhout J. In search of an understandable consensus algorithm[C]// USENIX Annual Technical Conference. [s.l.]: USENIX. 2014: 305-319.
[3] http://www.javashuo.com/article/p-uilcogdn-vk.html
[4] Raft原理動畫:http://thesecretlivesofdata.com/raft/