分佈式共識算法 (一) 背景html
分佈式共識算法 (三) Raft算法github
Raft 是一種爲了管理複製日誌的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但Raft更加容易理解和實踐,在工程領域的重要性毋庸置疑。注:本文是在研讀Raft算法論文後寫出,因原版論文太長,故提煉了一下重點,方便你們快速掌握。安全
區別於通常一致性算法,Raft算法的特性以下:服務器
強Leader:Raft 使用一種更強的領導形式。日誌只從Leader流向servers。這種方式簡化了對複製日誌的管理而且使得 Raft 算法更加易於理解。網絡
Leader選舉:Raft 算法使用一個隨機計時器來選舉Leader。這種方式只是在任何一致性算法都必須實現的心跳機制上增長了一點機制,這在解決衝突的時候會更加簡單快捷。併發
成員關係調整:Raft 使用一種共同一致的方法來處理集羣成員變換的問題,處於調整過程當中的兩種不一樣的配置集羣中大多數機器會有重疊,這就使得集羣在成員變換的時候依然能夠繼續工做。分佈式
共識算法一般出如今複製狀態機的上下文中。在這種方法中,一個server集羣上的多個State Machine計算相同狀態的相同副本,而且在一些機器宕掉的狀況下也能夠繼續運行。複製狀態機在分佈式系統中被用於解決不少容錯的問題。複製狀態機一般都是基於複製日誌實現的,結構以下圖:post
如上圖,每個server存儲一個包含一系列指令的log,而且State Machine順序執行log中的指令。每個Log都按照相同的順序包含相同的指令,因此每個server都執行相同的指令序列。由於每一個狀態機(state machines)都是肯定的,每一次執行操做都產生相同的狀態和一樣的結果序列。
Replicated state machines 流程:
爲了讓算法更加可理解,Raft的做者作了2方面的努力:
raft算法中每一個server可能存在3種身份:Leader領導者、Candidate候選者、Follower追隨者。
如上圖,一個Follower沒有收到來自Leader的心跳超時後,進入候選者Candidate身份,開始嘗試參與選舉。當收到超過1/2的選票時就變成Leader,併發送心跳給follower.
如上圖,Raft 把時間分割成任意長度的任期(term)。任期用連續的整數標記。每一段任期(term)從一次選舉(election)開始,一個或者多個候選人嘗試成爲領導者。若是一個候選人贏得選舉,而後他就在接下來的任期內充當領導人的職責。在某些狀況下,會形成選票的割裂spilt(達不到大部分的票)。在這種狀況下,這一任期會以沒有領導人結束;一個新的任期(和一次新的選舉)會很快從新開始。Raft 保證了在一個給定的任期內,最多隻有一個領導者。
Raft設定了5個原則,這些原則在任什麼時候候有效!!
Raft 將一致性問題分解成了三個相對獨立的子問題:領導者選舉(Leader election)、日誌複製(Log replication)、安全性(Safety)。
Raft 使用一種"心跳機制"來觸發領導人選舉。當服務器程序啓動時,他們都是跟隨者身份。只要從領導人或者候選者處接收到有效的 RPCs,這個服務器節點保持着跟隨者狀態。Leader週期性的向全部Follower發送心跳包來維持本身的權威。若是一個跟隨者在一段時間裏沒有接收到任何消息,也就是選舉超時,那麼他就會認爲系統中沒有可用的領導者,而且發起選舉以選出新的領導者。
要開始一次選舉過程,Follower先要增長本身的當前任期(term)號而且轉換到Candidate狀態。而後他會並行的向集羣中的其餘服務器節點發送請求投票的 RPCs 來給本身投票。候選人會繼續保持着當前狀態直到如下三件事情之一發生:
Raft 算法使用隨機選舉超時(例如 150-300 毫秒)的方法來確保不多會發生選票瓜分的狀況,就算髮生也能很快的解決。每個候選人在開始選舉時,會重置隨機的選舉超時時間,而後在超時時間內等待投票的結果;這樣減小了在新的選舉中另外的選票spilt的可能性。
一旦一個Leader被選舉出來,他就開始爲客戶端提供服務。客戶端的每個請求都包含一條被複制狀態機執行的指令。leader把這條指令添加進log做爲一個新entry,而後並行給其餘的服務器發AppendEntries RPCs ,讓他們複製這條entry。當這條log entry被安全的複製,leader會把entry添加進狀態機中而後把執行的結果返回給client。若是follower崩潰或者運行緩慢,再或者網絡丟包,leader會不斷的重複嘗試AppendEntries RPCs (儘管已經回覆了客戶端)直到全部的follower都最終存儲了全部的log entry。
如上圖,每個log entry存儲一條狀態機指令和從Leader收到這條指令時的任期號(term number)。每一條log entry都有一個index來代表它在日誌中的位置。
1.Leader一旦把建立的Log entry複製到大多數的server上的時候,log entry就會被提交commited(例如上圖中的條目 7)。Raft 算法保證全部已提交的日誌條目都是持久化的而且最終會被全部可用的狀態機執行。
2.同時,領導人的日誌中以前的全部日誌條目也都會被提交,包括由其餘領導人建立的條目。
3.一旦follower得知一個log entry已提交,follower就會把這個log entry添加進本地狀態機
Leader崩潰後可能致使日誌不一致,以下圖:
如上圖,當一個領導人成功當選時,跟隨者多是任何狀況(a-f)。每個盒子表示是一個log entry;裏面的數字表示任期號。跟隨者可能會缺乏一些log entry(a-b),可能會有一些未被提交的log entry(c-d),或者兩種狀況都存在(e-f)。例如,場景 f 可能會這樣發生,某服務器在任期 2 的時候是領導人,已附加了一些log entry到本身的日誌中,但在提交以前就崩潰了;很快這個機器就被重啓了,在任期 3 從新被選爲領導人,而且又增長了一些log entry到本身的日誌中;在任期 2 和任期 3 的日誌被提交以前,這個服務器又宕機了,而且在接下來的幾個任期裏一直處於宕機狀態。
解決方案:
要使得follower的日誌和本身一致,leader必須找到最後二者達成一致的地方,而後刪除從那個點以後的全部log entry,發送本身的日誌給follower。全部的這些操做都在進行AppendEntries RPCs 的一致性檢查時完成。leader針對每個跟隨者維護了一個 nextIndex,這表示下一個須要發送給跟隨者的log entry的index。當一個領導人剛得到權力的時候,他初始化全部的 nextIndex 值爲本身的最後一條日誌的index加1。若是一個跟隨者的日誌和領導人不一致,那麼在下一次的附加日誌 RPC 時的一致性檢查就會失敗。在被跟隨者拒絕以後,領導人就會減少 nextIndex 值並進行重試。最終 nextIndex 會在某個位置使得領導人和跟隨者的日誌達成一致。當這種狀況發生,AppendEntries RPC 就會成功,這時就會把跟隨者衝突的日誌條目所有刪除而且加上領導人的日誌。一旦AppendEntries RPC 成功,那麼跟隨者的日誌就會和領導人保持一致,而且在接下來的任期裏一直繼續保持。
Raft使用voting process來防止那些log不包含所有committed entry的candidate贏得選舉。candidate爲了贏得選舉必須和cluster的majority進行交互,這意味着每一個committed entry必須都在其中的一個majority存在。若是一個candidate的log至少和任何majority中的log保持up-to-date("up-to-date"將在下文精肯定義),那麼它就包含了全部committed entry。RequestVote RPC實現了這一約束:RPC中包含了candidate的log信息,若是它本身的log比該candidate的log更新,voter會拒絕投票。
如何比較哪一個新(up-to-date)?
Raft經過比較log中last entry的index和term來肯定兩個log哪一個更up-to-date。若是兩個log的last entry有不一樣的term,那麼擁有較大term的那個log更up-to-date。若是兩個log以相同的term結束,那麼哪一個log更長就更up-to-date。
如上圖,展現了爲何領導人沒法決定對老任期號的日誌條目進行提交。在 (a) 中,S1 是領導者,部分的複製了索引位置 2 的日誌條目。在 (b) 中,S1 崩潰了,而後 S5 在任期 3 裏經過 S三、S4 和本身的選票贏得選舉,而後從客戶端接收了一條不同的日誌條目放在了索引 2 處。而後到 (c),S5 又崩潰了;S1 從新啓動,選舉成功,開始複製日誌。在這時,來自任期 2 的那條日誌已經被複制到了集羣中的大多數機器上,可是尚未被提交。若是 S1 在 (d) 中又崩潰了,S5 能夠從新被選舉成功(經過來自 S2,S3 和 S4 的選票),而後覆蓋了他們在索引 2 處的日誌。反之,若是在崩潰以前,S1 把本身主導的新任期裏產生的日誌條目複製到了大多數機器上,就如 (e) 中那樣,那麼在後面任期裏面這些新的日誌條目就會被提交(由於S5 就不可能選舉成功)。 這樣在同一時刻就同時保證了,以前的全部老的日誌條目就會被提交。
解決方案:
爲了防止這樣問題的發生,Raft不會經過計算備份的數目,來提交以前term的log entry。只有leader的當前term的log entry纔會計算備份數並committed;一旦當前term的entry以這種方式被committed了,那麼以前的全部entry都將由於Log Matching Property而被間接committed。
這裏原論文是對Raft5個原則的第四個「領導者完整性(Leader Completeness Property)」的論證,咱們並不關心這個。
若是跟隨者或者候選人崩潰了,那麼後續發送給他們的 RPCs 都會失敗。Raft 就會無限的重試;若是崩潰的機器重啓了,就成功了。Raft 的 RPCs 都是冪等的,因此這樣重試不會形成任何問題。例如一個跟隨者若是收到附加日誌請求可是他已經包含了這一日誌,那麼他就會直接忽略這個新的請求。
咱們對於Raft的一個要求是,它的安全性不能依賴於時間:系統不會由於有些事件發生地比預期慢了或快了而產生錯誤的結果。然而,可用性(系統及時響應client的能力)將不可避免地依賴於時間。好比,由於server崩潰形成的信息交換的時間比一般狀況下來得長,candidate就不能停留足夠長的時間來贏得election;而沒有一個穩定的leader,Raft將不能進一步執行。
leader election是Raft中時間起最重要做用的地方。當系統知足如下的timing requirement的時候,Raft就可以選舉而且維護一個穩定的leader:
廣播時間(broadcastTime) << 選舉超時時間(electionTimeout) << 平均故障間隔時間(MTBF)
在這個不等式中,broadcastTime是server並行地向集羣中的每一個server發送RPC而且收到回覆的平均時間;electionTimeout就是選舉超時;MTBF是單個server發生故障的時間間隔。broadcastTime必須比electionTimeout小几個數量級,這樣leader就能可靠地發送heartbeat message從而防止follower開始選舉;經過隨機化的方法肯定electionTimeout,該不等式又讓split vote不太可能出現。electionTimeout必須比MTBF小几個數量級,從而讓系統能穩定運行。當leader崩潰時,系統會在大概一個electionTimeout裏不可用;咱們但願這隻佔整個時間的很小一部分。
broadcastTime和MTBF都是底層系統的特性,而electionTimeout是咱們必須選擇的。Raft的RPC一般要求接收者持久化信息到stable storage,所以broadcastTime的範圍在0.5ms到20ms之間,這取決於存儲技術。所以,electionTimeout能夠取10ms到500ms。一般,server的MTBF是幾個月或者更多,所以很容易知足timing requirement。
Raft 和 Paxos 最大的不一樣之處就在於 Raft 的強領導特性:Raft 使用領導人選舉做爲一致性協議裏必不可少的部分,而且將盡量多的功能集中到了領導人身上。這樣就可使得算法更加容易理解。目前Raft已應用到了 騰訊雲消息隊列(Cloud Message Queue,CMQ)上。
============參考===========
論文:In Search of an Understandable Consensus Algorithm (Extended Version)
博客:
https://blog.csdn.net/qq_36561697/article/details/88550398
https://www.cnblogs.com/YaoDD/p/6172011.html