學習Raft算法的筆記

Raft是一種爲了管理日誌複製的一致性算法。它提供了和Paxos算法相同的功能和性能,可是它的算法結構和Paxos不一樣,使得Raft算法更加容易理解而且更容易構建實際的系統。爲了提高可理解性,Raft將一致性算法分解成幾個關鍵的模塊,例如領導選舉,日誌複製和安全性。同時它經過實施一個更強的一致性來減小須要考慮的狀態和數量。從一個用戶研究的結果能夠證實,對於學生而言,Raft算法比Paxos算法更容易學。Raft算法還包括了新的機制來容許集羣成員的動態改變,讓利用重疊的大多數來保證安全性。git

Raft算法概述

Raft算法是從多副本狀態機的角度提出,用於管理多副本狀態機的日誌複製,它一致性分解爲多個子問題:領導選舉(Leader election),日誌複製(Log replication),安全性(Safety),日誌壓縮(Log compaction),成員變動(Menbership change)等。同時,Raft算法使用了更強的假設來減小須要考慮的狀態,使之變得更加容易理解。
Raft講系統中的角色分爲領導者(Leader),跟從者(Follower)和候選人(Candidate):
Leader: 接收客戶端請求,並向Follower同步請求日誌。當日志同步到大都數節點上後告告訴Follower提交日誌。
Follower:接收並持久化Leader同步的日誌,在Leader告訴它的日誌提交以後,提交日誌。
Candidate:Leader選舉過程當中的臨時角色。
Raft算法角色
Raft要求系統在任意時刻最多隻有一個Leader,正常工做期間Leader和Followers。
Raft算法角色狀態轉換以下:
Raft算法狀態轉換github

跟隨者只響應來自其餘服務器的請求。若是跟隨者接收不到消息,那麼它就會變成候選人併發起一次選舉。得到集羣中大多數選票的候選人將成爲領導者。在一個任期內,領導人直到本身宕機了。算法

Term.png

Raft算法時間被劃分紅爲一個個的任期(Term),每一個任期(Term)開始都是一次Leader選舉。選舉成功後,領導人會管理整個集羣直到任期(Term)結束。有時候會選舉失敗,那麼任期(Term)就會沒有領導者,而結束。任期(Term)之間的切換能夠在不一樣的時間不一樣的服務器上觀察到。安全

Raft算法中服務器之間節點通訊使用遠程調用(RPCs).而在etcd的實現當中v2版本用的http,而v3版本採用的是grpc(自己是跨平臺)。而且基本的一致性算法只是用了兩種類型的Rpcs。請求投票(RequestVote)RPCs由候選人發起。而後附加條目數(AppendEntries)由Leader發起。用來複制日誌和提供一種心跳機制。爲了服務器之間傳輸快照增長了第三種RPCs。當服務器沒有及時收到RPC的響應時,會進行重試,而且它們可以並行發起RPCs來獲取最佳性能。服務器

複製狀態機

一組服務器上的狀態機產生相同狀態的副本,而且在一些機器宕掉的狀況下也能夠繼續運行。複製狀態機在分佈式系統中被用於解決不少容錯的問題。例如,大規模的系統中一般都有一個集羣領導者,像 GFS、HDFS 和 RAMCloud,典型應用就是一個獨立的的複製狀態機去管理領導選舉和存儲配置信息而且在領導人宕機的狀況下也要存活下來。好比 Chubby 和 ZooKeeper。
複製狀態機網絡

複製狀態機的結構。一致性算法管理着來自客戶端指令的複製日誌。狀態機從日誌中處理相同順序的相同指令,因此產生的結果也是相同的。併發

複製狀態機一般都是基於複製日誌實現的,如圖 1。每個服務器存儲一個包含一系列指令的日誌,而且按照日誌的順序進行執行。每個日誌都按照相同的順序包含相同的指令,因此每個服務器都執行相同的指令序列。由於每一個狀態機都是肯定的,每一次執行操做都產生相同的狀態和一樣的序列。分佈式

保證複製日誌相同就是一致性算法的工做了。在一臺服務器上,一致性模塊接收客戶端發送來的指令而後增長到本身的日誌中去。它和其餘服務器上的一致性模塊進行通訊來保證每個服務器上的日誌最終都以相同的順序包含相同的請求,儘管有些服務器會宕機。一旦指令被正確的複製,每個服務器的狀態機按照日誌順序處理他們,而後輸出結果被返回給客戶端。所以,服務器集羣看起來造成一個高可靠的狀態機。性能

實際系統中使用的一致性算法一般含有如下特性:動畫

安全性保證(絕對不會返回一個錯誤的結果):在非拜占庭錯誤狀況下,包括網絡延遲、分區、丟包、冗餘和亂序等錯誤均可以保證正確。
可用性:集羣中只要有大多數的機器可運行而且可以相互通訊、和客戶端通訊,就能夠保證可用。所以,一個典型的包含 5 個節點的集羣能夠容忍兩個節點的失敗。服務器被中止就認爲是失敗。他們當有穩定的存儲的時候能夠從狀態中恢復回來並從新加入集羣。
不依賴時序來保證一致性:物理時鐘錯誤或者極端的消息延遲在可能只有在最壞狀況下才會致使可用性問題。
一般狀況下,一條指令能夠儘量快的在集羣中大多數節點響應一輪遠程過程調用時完成。小部分比較慢的節點不會影響系統總體的性能。

Leader選舉

Raft使用心跳(heartbeat)觸發Leader選舉。當服務器啓動時,初始化Follower。Leader向全部的Followers週期性的發送heartbeat。若是Follower在選舉超時時間內沒有收到Leader的heartbeat,就會等待一段隨機時間(150ms-300ms)發起一次選舉。

Follower先要增長本身的當前任期號,也就把當前的任期號加一而且轉換到候選人狀態。而後它們會並行的向集羣中的其餘服務器節點發起請求投票的RPCs來給本身投票。結果會有如下三種狀況:

  1. 它本身贏得了此次選舉
  2. 其餘的服務器成爲了領導者
  3. 一段世家以後沒有人獲取勝利的人。

日誌複製

Leader被選舉出來後.它就開始爲客戶端提供服務,客戶端每一個請求都包含一條被複制狀態機執行的命令。領導人將這條指令做爲新的日誌條目附加到日誌中去。而後並行的發起附加條目RPCs給其餘的服務器,讓它們複製這個日誌條目,當這條日誌條目被安全的複製。領導人會應用這條日誌條目到它的狀態中而後把執行的結果返回客戶端。若是Follower崩潰或者運行緩慢,再或者網絡丟包,領導人會不斷的嘗試附加日誌條目RPCs(儘管已經回覆了客戶端)直到全部Follower都最終存儲了全部條目數。
Raft日誌同步過程

日誌由有序編號(log index)的日誌組成條目。每一個日誌條目包含它被建立的任期號(term),和用於狀態機執行的命令。若是一個日誌條目被複制到大多數服務器上,就被認爲能夠提交了(commit)了。

Raft日誌存儲

Raft維護着一下特徵:
1.若是在不一樣的日誌中的兩個條目擁有相同的索引和任期號,那麼它們存儲了相同的指令。
2.若是在不一樣的日誌中的兩個條目擁有相同的索引和任期號,那麼他們的以前的全部日誌條目也所有相同。

第一個特新來這樣的一個事實,領導人最多在一個任期裏在指定的日誌索引位置建立一條日誌條目,同時日誌條目在日誌中的位置也歷來不會改變。第二個特性由附加日誌RPC的一個簡單一致性檢查保證。在發送附加日誌RPC的時候,領導人會把新的日誌條目緊接着以前的條目索引位置和任期號包含在裏面。若是跟隨者在它的日誌中找不到包含相同的日誌索引位置和任期號的條目,那麼他就會拒絕接收新的條目日誌。一致性檢查就像一個概括步驟:一開始空日誌狀態確定是知足日誌匹配特性的,而後一致性檢查保護了日誌匹配特性當日志擴展的時候。所以,每當附加日誌RPC返回成功時,領導人就知道跟隨着的日誌時同樣的了。
Leader和Followers上日誌不一致情形

當一個領導人成功當選時,跟隨者多是任何狀況(a-f)。每個盒子表示是一個日誌條目,裏面的數字表示任期號。跟隨者可能缺乏一些體制條目(a-b),可能會有一些未被提交的日誌條目(c-d),或者兩種狀況都存在的(e-f)。例如,場景f可能會發生,某些服務器在任期號2的時候是領導人,已附加了一些日誌條目到本身的日誌中,但在提交以前就就崩潰了,很快這個機器就被重啓了,在任期3從新被選爲領導人,而且又增長了一些日誌條目到本身的日誌中,而且又增長了一些日誌條目到本身的日誌中,在任期2和任期3的日誌被提交以前,這個服務器又宕機了,而且在接下來的幾個任期裏一直處於宕機狀態。

要使得跟隨着的日誌進入和本身一致的狀態,領導人必須找到最後二者達成一致的地方,而後刪除那個點以後的全部日誌條目,發送本身的日誌給跟隨者。全部的這些日誌操做都在進行附加日誌RPCs的一致性檢查時完成。領導人針對沒一個維護者維護了一個nextIndex,這表示下一個發送給追隨者的日誌條目的索引地址。當一個領導人剛得到領導者的權利的時候,他初始化全部的nextIndex值做爲本身的最後一條日誌的index加1。若是一個跟隨者的日誌和領導人不一致,那麼下一第二天志附RPC時的一致性檢查就會失敗。在被跟隨者拒絕以後,領導人就會減小nextIndex值並進行重試。最終nextIndex會在某個位置使得領導人和跟隨者的日誌達成一致。當這種狀況發生,附加日誌RPC就會成功,這時就會把跟隨者衝突的日誌條目所有刪除而且加上領導人的日誌。一旦附加日誌RPC成功,那麼跟隨者的日誌就會和領導人保持一直,而且在接下來的任期裏一直繼續保持。

安全性

Raft增長了以下兩條限制以保證安全性:
1>擁有最新的已提交的log entry的Follower纔有資格成爲Leader。
這個保證是在RequestVote RPC中作的,Candidate在發送RequestVote RPC時。要帶上本身的最後一條日誌的term和log Index。其餘節點收到消息時,若是發現本身的日誌請求中攜帶的更新,則拒絕投票。日誌比較的原則是:若是本地的最後一條log entry的term更大,則term大更新,若是term同樣大,則log Index更大的更新。

2.Leader只能推動commit Index來提交當前term已經複製最到最大服務器上的日誌,舊term日誌的日誌要等到提交當前的term的日誌來間接提交(log Index 小於commit Index的日誌被間接提交)
之因此要這樣,是由於可能會出現已提交的日誌被覆蓋的狀況:
已提交的日誌被覆蓋

如圖的時間序列展現了領導人沒法決定對老任期號的日誌條目進行提交。在(a)中,S1是Leader,部分的是複製了索引的位置2的條目數目。(b)是時期,S1崩潰了,而後S5在任期3裏經過S3,S4和本身的選票贏得選舉,而後從客戶端接收了一條不同的日誌條目放在了索引2處。而後到(c),S5崩潰了,S1從新啓動,選舉成功,開始日誌複製。在這個時候,來自任期2的那條日誌已經被複制到了集羣的大多數機器上,可是尚未被提交,若是S1在(d)時期中又崩潰了。S5能夠從新被選舉成功(經過來自S2,S3,S4的選票),而後覆蓋了他門在索引2處的日誌。反之,若是在崩潰以前,S1把本身主導的任期裏產生的日誌日條目複製到了大多數機器上,就如(e)中那樣。那麼在後面任期裏面這些新的日誌條目會被提交(由於S5就不可能選舉成功)。這牙膏在同一時刻就同時保證了,以前的全部老的日誌條目就會被提交。

時間和可用性

Raft的要求之一就是安全性不能依賴時間:整個系統不能由於某些事件運行的比預期快一點或者慢一點產生了錯誤的結果。可是,可用性(系統能夠及時的響應客戶端)不可避免的要依賴時間。例如,若是消息交換比服務器故障間隔時間長,候選人沒有足夠長的時間來贏得選舉,沒有一個穩定的領導人,Raft將沒法工做。
領導人選舉時Raft中對時間要求最爲關鍵的方面。Raft能夠選舉並維持一個穩定的領導人,只須要知足下面的時間要求:

廣播時間(broadcastTime) << 選舉時間(election Timeout) << 平均故障時間(MTBF)

在這個不等式中,廣播時間指的時從一個服務器並行的發送RPCs給集羣中的其餘服務器並接收平均時間,選舉超時時間(150ms-300ms)選舉超時時間限制,而後平均故障時間就是對於一臺服務器而言,兩次故障之間的平均時間。廣播時間必須比選舉超時時間小一個量級,這樣領導人才能發送穩定的心跳消息來阻止跟隨者開始進入選舉狀態,經過隨機化選舉超時時間的方法,整個不等式也使得選票瓜分的狀況變成不願能。選舉選舉超時時間要比平局故障時間間隔小上幾個數量級,這樣系統才能穩定的運行。當領導人崩潰後,整個系統會大約至關於超時時的時間裏不可用。咱們但願這種狀況在系統中國運行不多出現。

廣播時間和平均故障間隔時間是由系統決定的,可是選舉超時時間是咱們本身選擇的。Raft 的 RPCs 須要接收方將信息持久化的保存到穩定存儲中去,因此廣播時間大約是 0.5 毫秒到 20 毫秒,取決於存儲的技術。所以,選舉超時時間可能須要在 10 毫秒到 500 毫秒之間。大多數的服務器的平均故障間隔時間都在幾個月甚至更長,很容易知足時間的需求。

成員變動

成員變動是在集羣運行過程當中副本發生變化,如增長/減小副本數,節點替換等。
成員變動也是一個分佈式一致性的問題,既全部服務器對成員新成員達成一致。可是成員變動又有其特殊性,由於成員變動的一致性達成的過程當中,參與投票的過程會發生變化。

若是將成員變動當成通常的一致性問題,直接向Leader發送成員變動請求,Leader複製成員變動日誌,達成多數以後提交,各個服務器提交成員變動日誌後從日誌成員(Cold)切換到最新成員配置(Cnew的時刻不一樣.

成員變動不能影響服務的可用性,可是成員變動過程的某一時刻,可能出現Cold和Cnew中同時存在兩個不相交的多數派,進而可能選出兩個Leader,造成不一樣的決議,破壞安全性。
成員變動的某一時刻Cold和Cnew中同時存在兩個不相交的多數派
因爲成員變動的這一特殊性,成員變動不能當成通常的一致性問題去解決。

爲了解決這一問題.Raft提出了兩段的成員變動方法。集羣先成舊成員配置Cold切換到一個過分的配置,稱爲共同一致(joint consensus),共同一致時舊成員配置Cold和新成員配置Cnew的組合Cold U Cnew,一旦共同一致Cold U Cnew被提交,系統在切換到新成員配置Cnew。
Raft兩階段成員變動

一個配置切換的時間線。虛線表示已經被建立可是尚未被提交的條目,實線表示最後被提交的日誌條目。領導人首先建立了C-old
,new的配置條目在本身的日誌中,並提交到C-old,new中(C-old的大多數和c-new的大多數)。而後他建立C-new條目而且提交到C-new的大多數。這樣就不存在C-new和C-old同時作出決定的時間點。

在關於從新配置還有三個問題須要提出,第一個問題是,新的服務器額能初始化沒有存儲任何的日誌條目。當這些服務器以這種狀態加入到集羣中,那麼它們須要一段時間來更新追趕。這時還不能提交新的日誌條目。爲了不這種可用性的間隔時間Raft在配置更新的時候用了一種額外的階段,在這種階段,新的服務器以沒有投票權的身份加入集羣中來(領導人複製日誌給它們。可是不考慮它們是大多數)。一旦新的服務器追遇上了集羣中的集羣,從新配置能夠向上面描述同樣處理。

第二個問題,集羣的領導人可能不是新配置的一員。在這種狀況下,領導人就會在提交了C-new日誌後退位(回到追隨者狀態)。這意味着有這樣一段時間,領導人管理着集羣,可是不包括他本身,他複製日誌可是不把他本身算做大多數之一。當C-new被提交時,會發生領導人過分。由於這時時最新的配置能夠獨立工做時間點(將老是可以在C-new配置下選出新的Leader)。再此以前,可能只從C-old中選出領導人。

第三個問題是:移除再也不C-new中的服務器可能會擾亂集羣。這些服務器將不會再接收心跳。當選舉超時時,它們就會進行新的選舉過程。它們會發送擁有新的任期號的請求投票RPCs,這樣會致使當前的領導人退回成跟隨者狀態。新的領導人最終被選出來,可是被移除的服務器將會再次超時,而後這種過程再次重複,致使總體可用性大幅度降低。

爲了不這個問題,當服務器確認當前領導人存在時,服務器會忽略投票RPCs。特別的,當服務器再當前最小選舉超時時間內收到一個請求投票的RPC。他不會更新當前的任期號和投票號。這不會影響正常的選舉,每一個服務器在開始一次選舉以前,至少等待一個最小選舉超時時間。而後這有利於避免被移除的服務器的擾亂。若是領導人可以發送心跳給集羣,那麼他就不會更大的任期號廢黜。

日誌壓縮

在實際系統中,不能讓日誌無限增加,不然系統重啓時須要花很長的時間回放,從而影響可用性。Raft採用對整個系統進行snapshot來解決,snapshot以前的日誌均可以拋棄。
每一個副本獨立的對本身系統狀態進行snapshot,而且只能對已經提交的日誌進行snapshot。
Snapshot中包含如下內容:
1>日誌元數據:最後提交的log entry的log index和term。這兩個值在snapshot以後的第一條log entry的AppendEntriesRPC的完整性檢查的時候會被用上。
2> 系統當前狀態。

當Leader要發給某個日誌落後太多的Follower的log entry被丟棄,Leader會將snapshot發給Follower。或者新加入一臺機器時,也會發送snapshot給它。發送snapshot使用InstalledSnapshot RPC。

日誌壓縮

一個服務器用新的快照替換了從1到5的條目數,快照存儲了當前的狀態。快照中包含了最後的索引位置和任期號

作snapshot不要作的太頻繁,不然消耗磁盤帶寬,也不要作的太平凡,不然一點節點重啓要回放大量日誌,影響可用性。推薦當日組織達到某個固定的大小作一次snapshot。

作一次snapshot可能耗時過長,會影響正常日誌同步。能夠經過使用copy-on-write技術避免snapshot過程影響正常的日誌同步過程。

一個關於 Raft 一致性算法的濃縮總結(不包括成員變換和日誌壓縮)。

總結

參考:
http://thesecretlivesofdata.com/raft/(Raft動畫)
https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md(Raft論文翻譯)

相關文章
相關標籤/搜索