Raft一致性算法

Raft 也是一種分布式一致性算法(2PC、Proxy、ZAB)

一致性算法是從復(fù)制狀態(tài)機(jī)的背景下提出的。
一致性算法管理著來自客戶端指令的復(fù)制日志。狀態(tài)機(jī)從日志中處理相同順序的相同指令,所以產(chǎn)生的結(jié)果也是相同的


三個(gè)子問題

Raft 將分布式一致性問題分解成了三個(gè)相對(duì)獨(dú)立的子問題:

  • 選主:現(xiàn)存的領(lǐng)導(dǎo)人宕機(jī),一個(gè)新的領(lǐng)導(dǎo)人需要被選舉出來
  • 日志復(fù)制:領(lǐng)導(dǎo)人必須從客戶端接收日志然后復(fù)制到集群中的其他節(jié)點(diǎn),并且強(qiáng)制要求其他節(jié)點(diǎn)的日志保持和自己相同
  • 安全性:在 Raft 中安全性的關(guān)鍵是狀態(tài)機(jī)安全:如果有任何的服務(wù)器節(jié)點(diǎn)已經(jīng)應(yīng)用了一個(gè)確定的日志條目到它的狀態(tài)機(jī)中,那么其他服務(wù)器節(jié)點(diǎn)不能在同一個(gè)日志索引位置應(yīng)用一個(gè)不同的指令

大體流程

集群選擇出一個(gè)節(jié)點(diǎn)作為 leader,leader 節(jié)點(diǎn)負(fù)責(zé)接收客戶端的請(qǐng)求(日志),并負(fù)責(zé)把請(qǐng)求復(fù)制給所有的從節(jié)點(diǎn),保證節(jié)點(diǎn)之間數(shù)據(jù)的同步。如果 leader 節(jié)點(diǎn)出現(xiàn)問題掛掉,那么其他正常節(jié)點(diǎn)會(huì)重新選擇 leader。

選主 leader election

每個(gè)節(jié)點(diǎn)在任意情況下,只能有三種狀態(tài)可選:

  • leader:領(lǐng)導(dǎo)節(jié)點(diǎn),或者主節(jié)點(diǎn),用來處理客戶端發(fā)來的請(qǐng)求,并保證請(qǐng)求數(shù)據(jù)在整個(gè)集群的同步,需要用心跳和 follower 節(jié)點(diǎn)通信,通知它們自己的可用性
  • follower:負(fù)責(zé)處理 leader 和 candidate 請(qǐng)求的節(jié)點(diǎn)。如果客戶端把請(qǐng)求發(fā)送給 follower 節(jié)點(diǎn),它需要把請(qǐng)求轉(zhuǎn)發(fā)給 leader,由 leader 統(tǒng)一負(fù)責(zé)管理
  • candidate:leader 的候選人,只是在選舉過程中短暫出現(xiàn)的狀態(tài)。如果通過選舉,則會(huì)變成 leader;如果選舉失敗,還是會(huì)回到 follower 狀態(tài)

任期(Term)的概念:任期是依次遞增的編號(hào),每次選舉都是一個(gè)新的任期。在一個(gè)任期內(nèi)最多只能有一個(gè) leader,也就是說一個(gè)任期可以有一個(gè) leader,表示正常工作;也可以沒有 leader,表明選舉失敗。某個(gè)節(jié)點(diǎn)選舉成功后,就成為當(dāng)前任期的 leader,負(fù)責(zé)日志復(fù)制工作。

任期的主要目的是保證所有節(jié)點(diǎn)邏輯時(shí)間上的一致,而不會(huì)出現(xiàn)過期的請(qǐng)求導(dǎo)致邏輯混亂的情況。


每個(gè)節(jié)點(diǎn)都會(huì)保存一個(gè)當(dāng)前任期的值,當(dāng)節(jié)點(diǎn)通信時(shí)會(huì)交互當(dāng)前任期的值,

  • 如果節(jié)點(diǎn)發(fā)現(xiàn)其他節(jié)點(diǎn)的當(dāng)前任期比自己的大,就更新自己當(dāng)前任期的值;
  • 如果 leader 節(jié)點(diǎn)發(fā)現(xiàn)有比自己大的任期值,則知道自己的任期過期了,集群中有更新的 leader 節(jié)點(diǎn),它立即變成 follower 狀態(tài);
  • 如果節(jié)點(diǎn)接收到歷史任期的請(qǐng)求,則直接無視(這很可能是因?yàn)榫W(wǎng)絡(luò)延遲或者報(bào)文重復(fù)導(dǎo)致的)。

當(dāng)節(jié)點(diǎn)剛啟動(dòng)的時(shí)候,默認(rèn)是在 follower 狀態(tài)

  • 如果它能定時(shí)收到 leader 的心跳或者日志復(fù)制的請(qǐng)求,則會(huì)一直處于該狀態(tài);
  • 如果在設(shè)定的超時(shí)時(shí)間(election timeout)內(nèi)沒有收到 leader 的消息,則認(rèn)為當(dāng)前集群沒有 leader,或者 leader 以及失效,立即會(huì)發(fā)起重新投票
投票

投票開始,節(jié)點(diǎn)會(huì)增加自己的當(dāng)前任期值,轉(zhuǎn)換成 candidate 狀態(tài),并向其他節(jié)點(diǎn)發(fā)送請(qǐng)求投票的消息(表示自己想成為下一個(gè)任期的 leader)。有三種情況:

  • 節(jié)點(diǎn)收到大多數(shù)節(jié)點(diǎn)的投票,成為新任期的 leader。每個(gè)節(jié)點(diǎn)在每個(gè)任期只能投票一次,采取先到先得的原則,投票給最先收到的選主節(jié)點(diǎn)。大多數(shù)原則保證一個(gè)任期最多只能有一個(gè)節(jié)點(diǎn)
  • 節(jié)點(diǎn)發(fā)現(xiàn)已經(jīng)有另一個(gè)節(jié)點(diǎn)成為 leader。在等待選舉結(jié)果的時(shí)候,節(jié)點(diǎn)收到了心跳或者日志復(fù)制的消息(也就是有 leader 了),如果這個(gè) leader 合法(任期比自己的當(dāng)前任期高),則當(dāng)前節(jié)點(diǎn)會(huì)自動(dòng)變成 follower 狀態(tài);否則會(huì)繼續(xù)等待
  • 一段時(shí)間過去,并沒有任何節(jié)點(diǎn)成為 leader。比如有多個(gè)節(jié)點(diǎn)要選舉 leader,而且都投票結(jié)果比較分散,沒有節(jié)點(diǎn)獲得過半的票數(shù)

如果不采取任何措施,那么第三種情況一直出現(xiàn),會(huì)導(dǎo)致整個(gè)集群一直處于選舉的狀態(tài),這當(dāng)然是不可接受的。為此,raft 采取了隨機(jī)時(shí)間的辦法

  • 首先,選舉超時(shí)時(shí)間(election timeout)是隨機(jī)的,保證會(huì)有一個(gè)節(jié)點(diǎn)首先超時(shí),率先選舉,其他節(jié)點(diǎn)來不及和它競(jìng)選,它就會(huì)成為新的 leader,發(fā)送心跳和日志復(fù)制請(qǐng)求。

  • 其次,在開始選舉時(shí),每個(gè) candidate 節(jié)點(diǎn)重置它的超時(shí)計(jì)時(shí)器,等待計(jì)時(shí)器結(jié)束之后才會(huì)開始下一次選舉,從而打亂下次選舉的前后順序,保證有一個(gè)節(jié)點(diǎn)先開始選舉,并成為 leader。

事實(shí)證明,這兩種方式能夠保證選舉在很短的時(shí)間里完成,而不會(huì)一直循環(huán)。
選舉過期時(shí)間(election timeout)一般設(shè)置為 150-300ms,這是大量實(shí)驗(yàn)得到的經(jīng)驗(yàn)值

日志復(fù)制 Log replication

一旦一個(gè)領(lǐng)導(dǎo)人被選舉出來,他就開始為客戶端提供服務(wù)。客戶端的每一個(gè)請(qǐng)求都包含一條被復(fù)制狀態(tài)機(jī)執(zhí)行的指令。

log同步復(fù)制

  • 領(lǐng)導(dǎo)人把這條指令作為一條新的日志條目附加到日志中去,
  • 然后并行的發(fā)起附加條目 RPCs 給其他的服務(wù)器,讓他們復(fù)制這條日志條目。
  • 當(dāng)這條日志條目被安全的復(fù)制(下面會(huì)介紹),領(lǐng)導(dǎo)人會(huì)應(yīng)用這條日志條目到它的狀態(tài)機(jī)中
  • 然后把執(zhí)行的結(jié)果返回給客戶端。
  • 如果跟隨者崩潰或者運(yùn)行緩慢,再或者網(wǎng)絡(luò)丟包,領(lǐng)導(dǎo)人會(huì)不斷的重復(fù)嘗試附加日志條目 RPCs (盡管已經(jīng)回復(fù)了客戶端)直到所有的跟隨者都最終存儲(chǔ)了所有的日志條目。

每個(gè)日志記錄都要保存

  • 一個(gè)狀態(tài)機(jī)的指令,
  • 主節(jié)點(diǎn)接受請(qǐng)求時(shí)候的任期值
  • 一個(gè) index 表示它在日志文件中的位置

log提交

當(dāng)日志記錄被狀態(tài)機(jī)執(zhí)行后,就稱它為已提交(commited)。當(dāng)主節(jié)點(diǎn)知道日志記錄已經(jīng)復(fù)制到大多數(shù)節(jié)點(diǎn)時(shí),會(huì)把當(dāng)前記錄提交到本地的狀態(tài)機(jī)(因?yàn)槿罩疽呀?jīng)更新到大多數(shù)節(jié)點(diǎn),所有數(shù)據(jù)是安全的),也就是更改數(shù)據(jù)的值。

leader 節(jié)點(diǎn)會(huì)記錄已經(jīng)提交(commited)的最大日志 index,然后后續(xù)的心跳和日志復(fù)制請(qǐng)求會(huì)帶上這個(gè)值,這樣從節(jié)點(diǎn)就能知道哪些記錄已經(jīng)提交了,自己也會(huì)讓狀態(tài)機(jī)開始執(zhí)行日志中的記錄。從而達(dá)到所有狀態(tài)機(jī)數(shù)據(jù)的一致性!

這樣的日志機(jī)制保證了如果不同節(jié)點(diǎn)的日志文件某個(gè)記錄的 index 和任期都相同,那么它們的內(nèi)容也一定相同,而且之前的日志記錄也一定是一樣的。

當(dāng)主節(jié)點(diǎn)發(fā)送日志復(fù)制的請(qǐng)求時(shí),它會(huì)帶上前一個(gè)日志記錄的 index 和 term,如果從節(jié)點(diǎn)發(fā)現(xiàn)自己的日志中不存在這個(gè)記錄,則會(huì)拒絕這個(gè)請(qǐng)求。

log不一致

領(lǐng)導(dǎo)人崩潰的情況會(huì)使得日志處于不一致的狀態(tài)(老的領(lǐng)導(dǎo)人可能還沒有完全復(fù)制所有的日志條目)。這種不一致問題會(huì)在領(lǐng)導(dǎo)人和跟隨者的一系列崩潰下加劇。如跟隨者可能會(huì)丟失一些在新的領(lǐng)導(dǎo)人中有的日志條目,他也可能擁有一些領(lǐng)導(dǎo)人沒有的日志條目,或者兩者都發(fā)生。丟失或者多出日志條目可能會(huì)持續(xù)多個(gè)任期。

解決辦法:在 Raft 算法中,領(lǐng)導(dǎo)人處理不一致是通過強(qiáng)制跟隨者直接復(fù)制自己的日志來解決了(先找到從節(jié)點(diǎn)日志和自己日志記錄第一個(gè)不一致的地方,然后一直覆蓋到最后。)。這意味著在跟隨者中的沖突的日志條目會(huì)被領(lǐng)導(dǎo)人的日志覆蓋。5.4 節(jié)會(huì)闡述如何通過增加一些限制來使得這樣的操作是安全的。

安全性

raft 對(duì)選主做出了限制,從而實(shí)現(xiàn)算法的正確性。總的來說,這個(gè)限制只有一句話:只有保存了最新日志的節(jié)點(diǎn)(term最大 index最大的所有的已提交日志記錄)才能選舉成為 leader

跟隨者和候選人崩潰

如果跟隨者或者候選人崩潰了,那么后續(xù)發(fā)送給他們的 RPCs 都會(huì)失敗。Raft 中處理這種失敗就是簡(jiǎn)單的通過無限的重試;如果崩潰的機(jī)器重啟了,那么這些 RPC 就會(huì)完整的成功。如果一個(gè)服務(wù)器在完成了一個(gè) RPC,但是還沒有響應(yīng)的時(shí)候崩潰了,那么在他重新啟動(dòng)之后就會(huì)再次收到同樣的請(qǐng)求。
Raft 的 RPCs 都是冪等的,所以這樣重試不會(huì)造成任何問題。

集群成員變化

任何服務(wù)器直接從舊的配置直接轉(zhuǎn)換到新的配置的方案都是不安全的。一次性自動(dòng)的轉(zhuǎn)換所有服務(wù)器是不可能的,所以在轉(zhuǎn)換期間整個(gè)集群存在劃分成兩個(gè)獨(dú)立的大多數(shù)群體的可能性


為了保證安全性,配置更改必須使用兩階段方法。
在 Raft 中,集群先切換到一個(gè)過渡的配置,我們稱之為共同一致;一旦共同一致已經(jīng)被提交了,那么系統(tǒng)就切換到新的配置上。共同一致是老配置和新配置的結(jié)合:

  • 日志條目被復(fù)制給集群中新、老配置的所有服務(wù)器。
  • 新、舊配置的服務(wù)器都可以成為領(lǐng)導(dǎo)人。
  • 達(dá)成一致(針對(duì)選舉和提交)需要分別在兩種配置上獲得大多數(shù)的支持。

共同一致允許獨(dú)立的服務(wù)器在不影響安全性的前提下,在不同的時(shí)間進(jìn)行配置轉(zhuǎn)換過程。此外,共同一致可以讓集群在配置轉(zhuǎn)換的過程人依然響應(yīng)客戶端的請(qǐng)求。

日志壓縮

Raft 的日志在正常操作中不斷的增長(zhǎng),但是在實(shí)際的系統(tǒng)中,日志不能無限制的增長(zhǎng)。
快照是最簡(jiǎn)單的壓縮方法。

客戶端交互

Raft 中的客戶端發(fā)送所有請(qǐng)求給領(lǐng)導(dǎo)人。當(dāng)客戶端啟動(dòng)的時(shí)候,他會(huì)隨機(jī)挑選一個(gè)服務(wù)器進(jìn)行通信

  • 如果客戶端第一次挑選的服務(wù)器不是領(lǐng)導(dǎo)人,那么那個(gè)服務(wù)器會(huì)拒絕客戶端的請(qǐng)求并且提供他最近接收到的領(lǐng)導(dǎo)人的信息(附加條目請(qǐng)求包含了領(lǐng)導(dǎo)人的網(wǎng)絡(luò)地址)。
  • 如果領(lǐng)導(dǎo)人已經(jīng)崩潰了,那么客戶端的請(qǐng)求就會(huì)超時(shí);客戶端之后會(huì)再次重試隨機(jī)挑選服務(wù)器的過程。

Raft 的目標(biāo)是要實(shí)現(xiàn)線性化語(yǔ)義(每一次操作立即執(zhí)行,只執(zhí)行一次,在他調(diào)用和收到回復(fù)之間)
但是,如上述,Raft 是可以執(zhí)行同一條命令多次的:例如,如果領(lǐng)導(dǎo)人在提交了這條日志之后,但是在響應(yīng)客戶端之前崩潰了,那么客戶端會(huì)和新的領(lǐng)導(dǎo)人重試這條指令,導(dǎo)致這條命令就被再次執(zhí)行了。解決方案就是客戶端對(duì)于每一條指令都賦予一個(gè)唯一的序列號(hào)。然后,狀態(tài)機(jī)跟蹤每條指令最新的序列號(hào)和相應(yīng)的響應(yīng)。如果接收到一條指令,它的序列號(hào)已經(jīng)被執(zhí)行了,那么就立即返回結(jié)果,而不重新執(zhí)行指令。

只讀的操作可以直接處理而不需要記錄日志。但是,在不增加任何限制的情況下,這么做可能會(huì)冒著返回臟數(shù)據(jù)的風(fēng)險(xiǎn),因?yàn)轭I(lǐng)導(dǎo)人響應(yīng)客戶端請(qǐng)求時(shí)可能已經(jīng)被新的領(lǐng)導(dǎo)人作廢了,但是他還不知道。
線性化的讀操作必須不能返回臟數(shù)據(jù),Raft 需要使用兩個(gè)額外的措施在不使用日志的情況下保證這一點(diǎn)。

  • 首先,領(lǐng)導(dǎo)人必須有關(guān)于被提交日志的最新信息。領(lǐng)導(dǎo)人完全特性保證了領(lǐng)導(dǎo)人一定擁有所有已經(jīng)被提交的日志條目,但是在他任期開始的時(shí)候,他可能不知道那些是已經(jīng)被提交的。為了知道這些信息,他需要在他的任期里提交一條日志條目。Raft 中通過領(lǐng)導(dǎo)人在任期開始的時(shí)候提交一個(gè)空白的沒有任何操作的日志條目到日志中去來實(shí)現(xiàn)。
  • 第二,領(lǐng)導(dǎo)人在處理只讀的請(qǐng)求之前必須檢查自己是否已經(jīng)被廢黜了(他自己的信息已經(jīng)變臟了如果一個(gè)更新的領(lǐng)導(dǎo)人被選舉出來)。Raft 中通過讓領(lǐng)導(dǎo)人在響應(yīng)只讀請(qǐng)求之前,先和集群中的大多數(shù)節(jié)點(diǎn)交換一次心跳信息來處理這個(gè)問題。可選的,領(lǐng)導(dǎo)人可以依賴心跳機(jī)制來實(shí)現(xiàn)一種租約的機(jī)制,但是這種方法依賴時(shí)間來保證安全性

Ref:
動(dòng)畫解釋:http://thesecretlivesofdata.com/raft/
中文:https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md
參考:https://cizixs.com/2017/12/04/raft-consensus-algorithm/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評(píng)論 1 280
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,347評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • 尋找一種易于理解的一致性算法(擴(kuò)展版) 摘要 Raft 是一種為了管理復(fù)制日志的一致性算法。它提供了和 Paxos...
    枝葉君閱讀 2,656評(píng)論 0 15
  • 很久之前研究過raft協(xié)議,最近項(xiàng)目中一直沒有使用,有些生疏了,這次重溫了一下raft,花了兩天的時(shí)間,就順便做下...
    何約什閱讀 20,571評(píng)論 18 27
  • 尋找一種易于理解的一致性算法(擴(kuò)展版) 摘要 Raft 是一種為了管理復(fù)制日志的一致性算法。它提供了和 Paxos...
    yflau閱讀 1,020評(píng)論 0 1
  • 項(xiàng)目中使用ETCD來實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)和配置信息的存儲(chǔ),最近我抽空研究了一下ETCD和背后的一致性算法 — Raft算法...
    11舍的華萊士閱讀 16,978評(píng)論 1 37
  • 我們一起騎單車,沒有后座。我坐在橫杠,你騎車。 不知道為什么我們要一起去找三個(gè)地方洗澡。我們就沿著小河騎啊騎啊。 ...
    一棵芥菜閱讀 172評(píng)論 0 0