Raft分布式一致性算法(論文解讀和源碼實(shí)現(xiàn))

前言

建議先對raft論文有一些基本的瀏覽撕蔼,然后再看下面的內(nèi)容』蛄矗可以結(jié)合后面引用的鏈接去進(jìn)行更深入的學(xué)習(xí)惫恼。下文提到的章節(jié),指論文的章節(jié)

正文

通過領(lǐng)導(dǎo)人的方式澳盐,Raft 將一致性問題分解成了三個(gè)相對獨(dú)立的子問題:

  • 領(lǐng)導(dǎo)選舉:一個(gè)新的領(lǐng)導(dǎo)人需要被選舉出來祈纯,當(dāng)現(xiàn)存的領(lǐng)導(dǎo)人宕機(jī)的時(shí)候會(huì)重新選出一個(gè)新的領(lǐng)導(dǎo)(章節(jié) 5.2)
  • 日志復(fù)制:領(lǐng)導(dǎo)人必須從客戶端接收日志然后復(fù)制到集群中的其他節(jié)點(diǎn),并且強(qiáng)制要求其他節(jié)點(diǎn)的日志保持和自己相同叼耙。
  • 安全性:在 Raft 中安全性的關(guān)鍵是在圖 3 中展示的狀態(tài)機(jī)安全:如果有任何的服務(wù)器節(jié)點(diǎn)已經(jīng)應(yīng)用了一個(gè)確定的日志條目到它的狀態(tài)機(jī)中腕窥,那么其他服務(wù)器節(jié)點(diǎn)不能在同一個(gè)日志索引位置應(yīng)用一個(gè)不同的指令。章節(jié) 5.4 闡述了 Raft 算法是如何保證這個(gè)特性的筛婉;這個(gè)解決方案涉及到一個(gè)額外的選舉機(jī)制(5.2 節(jié))上的限制簇爆。

除此以外,還有:

  • 日志壓縮

  • 成員關(guān)系變化

  • 與客戶端交互

圖3
領(lǐng)導(dǎo)選舉

重點(diǎn)閱讀論文的 5.1 以及 5.2,結(jié)合 Figure 2 食用入蛆。

image.png

只需要兩個(gè)rpc和兩個(gè)周期性檢查的timer(heartbeat timer和election timer)即可描述領(lǐng)導(dǎo)選舉:

  • requestVote

  • appendEntity(作為心跳包)

  • election timer作為選舉超時(shí)(隨機(jī)超時(shí)時(shí)間150ms~300ms)响蓉,使得follwer轉(zhuǎn)變?yōu)閏andidate以及candidate處于分票狀態(tài)時(shí)的重新選舉。

  • heartbeat timer作為心跳超時(shí)(定時(shí)哨毁,50ms)枫甲,使用appendEntity重置各follower的election timer。(日志復(fù)制那里還要維護(hù)nextIndex扼褪,matchIndex和commitIndex)

對于每一個(gè)不同狀態(tài)的 Raft 節(jié)點(diǎn)言秸,他們可能有如下并發(fā)的事件:(即復(fù)制狀態(tài)機(jī)的行為)

  • Follower

    • 處理 AppendEntries(重置electionTimer,維護(hù)3個(gè)index迎捺,同步日志)
    • 處理 RequestVote(是否給發(fā)送請求的candidate投票举畸,需要candidate數(shù)據(jù)是up to date且獲得絕大多數(shù)節(jié)點(diǎn)的投票才能稱為leader)
    • electionTimer 超時(shí)(follower轉(zhuǎn)變?yōu)閏andidate)
  • Candidate

    • 處理 AppendEntries(判斷term是否轉(zhuǎn)變?yōu)閒ollower)
    • 處理 RequestVote(根據(jù)term是否需要轉(zhuǎn)變?yōu)閒ollower,判斷是否成為leader)
    • electionTimer 超時(shí)(分票后的重新選舉)
    • 發(fā)起投票 sendRequestVote(給集群其他節(jié)點(diǎn)發(fā)送requestVote凳枝,請求投票)
  • Leader

    • 處理 AppendEntries(判斷term是否轉(zhuǎn)變?yōu)閒ollower抄沮,維護(hù)各follower的nextIndex和matchIndex,維護(hù)leader的commitIndex)
    • 處理 RequestVote(判斷term是否轉(zhuǎn)變?yōu)閒ollower)
    • heartbeatTimer 超時(shí)(給各節(jié)點(diǎn)發(fā)送appendEntity)
    • 停掉electionTimer計(jì)時(shí)器
    • 發(fā)送心跳包 sendAppendEntries(根據(jù)leader的nextIndex數(shù)組打包請求參數(shù)岖瑰,發(fā)送appendEntries)
日志復(fù)制
image.png
image.png
圖示:
  • 黑點(diǎn):表示matchIndex

  • 箭頭:表示nextIndex

  • 虛線(虛線圍起來的叛买,紫色底的2。表示日志項(xiàng)):表示該日志還沒提交蹋订。

  • 實(shí)線(實(shí)線圍起來的):日志已提交

步驟:

當(dāng)前l(fā)eader通過心跳(appendEntities)維護(hù)不同節(jié)點(diǎn)的nextIndex和matchIndex率挣。

follower節(jié)點(diǎn)接收到appendEntity請求時(shí),對比自己的和leader發(fā)過來的term和nextIndex露戒。如果自己的小椒功,就返回false,讓leader降低nextIndex智什。直到leader發(fā)過來的日志就是自己缺失的那部分动漾,直接刪掉follower節(jié)點(diǎn)的matchIndex之后的數(shù)據(jù),添加leader發(fā)過來的自己缺失的數(shù)據(jù)荠锭。

leader節(jié)點(diǎn)接受到appendEntity的返回后:

如果是成功的返回:維護(hù)該follower的matchIndex和nextIndex旱眯,查看當(dāng)前是否絕大多數(shù)follower節(jié)點(diǎn)已復(fù)制(通過查看leader維護(hù)的matchIndex數(shù)組來判斷),如果大多數(shù)已復(fù)制证九,即表示該日志項(xiàng)已提交删豺,下一次心跳的AppendEntity會(huì)使得各follower的commitIndex也得到更新,即各follower節(jié)點(diǎn)的該日志項(xiàng)也處于已提交狀態(tài)愧怜。呀页;

如果是失敗的返回,降低對應(yīng)follower的nextIndex叫搁,然后直接對該follower重新發(fā)送appendEntity

image.png
image.png
image.png
image.png

此時(shí)該復(fù)制狀態(tài)機(jī)的狀態(tài)就保持了一致(獲得共識(shí))赔桌。

安全性

詳見論文第五節(jié)

描述了 Raft 算法是如何選舉和復(fù)制日志的。然而渴逻,到目前為止描述的機(jī)制并不能充分的保證每一個(gè)狀態(tài)機(jī)會(huì)按照相同的順序執(zhí)行相同的指令疾党。

  • 選舉限制

    Raft 使用投票的方式來阻止一個(gè)候選人贏得選舉除非這個(gè)候選人包含了所有已經(jīng)提交的日志條目。候選人為了贏得選舉必須聯(lián)系集群中的大部分節(jié)點(diǎn)惨奕,這意味著每一個(gè)已經(jīng)提交的日志條目在這些服務(wù)器節(jié)點(diǎn)中肯定存在于至少一個(gè)節(jié)點(diǎn)上雪位。如果候選人的日志至少和大多數(shù)的服務(wù)器節(jié)點(diǎn)一樣新(up to date,Raft 通過比較兩份日志中最后一條日志條目的索引值和任期號定義誰的日志比較新)梨撞,那么他一定持有了所有已經(jīng)提交的日志條目雹洗。

  • 提交之前任期內(nèi)的日志條目

    Raft 永遠(yuǎn)不會(huì)通過計(jì)算副本數(shù)目的方式去提交一個(gè)之前任期內(nèi)的日志條目。只有領(lǐng)導(dǎo)人當(dāng)前任期里的日志條目通過計(jì)算副本數(shù)目可以被提交卧波;一旦當(dāng)前任期的日志條目以這種方式被提交时肿,那么由于日志匹配特性,之前的日志條目也都會(huì)被間接的提交港粱。

成員關(guān)系變化

詳見論文第6小節(jié)螃成。

image.png

兩階段變更的方式:

第一階段:共同一致(過渡階段)

  • 領(lǐng)導(dǎo)者收到從舊配置到新配置的變更請求時(shí),創(chuàng)建共同一致的日志并復(fù)制給其他節(jié)點(diǎn)

  • follower以最新的配置做決定查坪,領(lǐng)導(dǎo)者需要以已經(jīng)提交的配置來做決定

  • 新舊配置中所有機(jī)器都可能成為領(lǐng)導(dǎo)者

  • 達(dá)成一致要在新舊配置上均獲得大多數(shù)支持

第二階段:切換到新配置

  • 提交新配置的日志到所有節(jié)點(diǎn)寸宏,一旦新配置的日志被提交,即完成變更
日志壓縮

詳見論文第7小節(jié)

在實(shí)際的系統(tǒng)中偿曙,Raft 產(chǎn)生的日志在持續(xù)的正常操作中不斷增長氮凝,但不可以無限的增長下去。

快照(snapshot)是最簡單的壓縮方式望忆。在快照中罩阵,全部的當(dāng)前系統(tǒng)狀態(tài)都被寫入到快照中,存儲(chǔ)到持久化的存儲(chǔ)中启摄,然后在那個(gè)時(shí)刻之前的全部日志都可以被丟棄永脓。

Raft快照基本思想:

  • 每個(gè)服務(wù)器獨(dú)立創(chuàng)建快照,只包括已經(jīng)被提交的日志

  • 快照值存儲(chǔ)了當(dāng)前的狀態(tài)鞋仍、最后的索引位置和任期號

  • 快照完成后常摧,刪除最后索引位置之前的所有日志和快照

  • 領(lǐng)導(dǎo)人必須偶爾的發(fā)送快照給一些落后的follower

異常情況分析

image.png

上圖的任意一步,任意一個(gè)節(jié)點(diǎn)都可以宕機(jī)威创。也可以保證數(shù)據(jù)一致性落午。。

follower或者candidate節(jié)點(diǎn)崩潰
  • 如果不是一半節(jié)點(diǎn)崩潰肚豺,服務(wù)還是可用溃斋,leader還是可以通過appendEntity給正常follower節(jié)點(diǎn)復(fù)制數(shù)據(jù),確認(rèn)接收后吸申,leader保證該日志項(xiàng)是提交狀態(tài)梗劫,向客戶端返回操作成功享甸,等到心跳(appendEntity)使得follower對于該日志項(xiàng)也提交成功;

  • 如果超過一半節(jié)點(diǎn)崩潰梳侨,服務(wù)不可用蛉威,等待節(jié)點(diǎn)重啟,通過appendEntity掃描節(jié)點(diǎn)需要同步的數(shù)據(jù)(維護(hù)nextIndex走哺,matchIndex)蚯嫌,使用leader的數(shù)據(jù)覆蓋當(dāng)前follower數(shù)據(jù),即該節(jié)點(diǎn)已同步完成數(shù)據(jù)丙躏,等待多數(shù)節(jié)點(diǎn)恢復(fù)择示,服務(wù)即可用。

客戶端請求的數(shù)據(jù)到達(dá)leader前晒旅,leader崩潰

等待集群重新選舉栅盲。且客戶端重新請求。

客戶端數(shù)據(jù)已到達(dá)leader废恋,但未復(fù)制到絕大多數(shù)follower(即處于uncommited)剪菱,leader崩潰

uncommited數(shù)據(jù)無意義。等待集群重新選舉拴签。且客戶端重新請求孝常。

客戶端數(shù)據(jù)已到達(dá)leader,已復(fù)制到絕大多數(shù)follower(處于commited)蚓哩,leader崩潰

等待集群重新選舉构灸。且客戶端重新請求。但是該不應(yīng)該繼續(xù)保存在復(fù)制狀態(tài)機(jī)上岸梨。(不然就造成重復(fù)提交)喜颁。因此需要raft服務(wù)能內(nèi)部去重(具備冪等性,實(shí)現(xiàn)線性化語義)曹阔,論文上是第八節(jié)(client interaction)中提到一個(gè)解決方案就是:

客戶端對于每一條指令都賦予一個(gè)唯一的序列號半开。然后,狀態(tài)機(jī)跟蹤每條指令最新的序列號和相應(yīng)的響應(yīng)赃份。如果接收到一條指令寂拆,它的序列號已經(jīng)被執(zhí)行了,那么就立即返回結(jié)果抓韩,而不重新執(zhí)行指令纠永。

網(wǎng)絡(luò)分區(qū),導(dǎo)致雙leader

客戶端向那個(gè)不具備絕大多數(shù)節(jié)點(diǎn)的leader請求谒拴,肯定是超時(shí)的尝江。

客戶端只有向具備絕大多數(shù)節(jié)點(diǎn)的leader請求才能正常返回。

多說一下英上,分區(qū)后的數(shù)據(jù)恢復(fù):

不正常的leader網(wǎng)絡(luò)恢復(fù)炭序,接收到正常leader的appendEntity后降級為follower啤覆,進(jìn)行日志復(fù)制過程。

代碼實(shí)現(xiàn)raft

自己實(shí)現(xiàn)過一個(gè)簡單的raft惭聂,包含領(lǐng)導(dǎo)選舉窗声,日志復(fù)制和安全性,未實(shí)現(xiàn)日志壓縮彼妻,成員關(guān)系變化嫌佑。

思路:

一個(gè)節(jié)點(diǎn)使用了一個(gè)go routine模擬一個(gè)狀態(tài)機(jī)豆茫,兩個(gè)timer侨歉,兩個(gè)rpc(使用go的net/rpc內(nèi)置包)。

但是發(fā)現(xiàn)測試特別困難揩魂。僅僅根據(jù)論文描述寫了代碼幽邓,也簡單的打印測試,基本是可用的火脉,可以選舉出來leader牵舵,同步數(shù)據(jù),但是異常邊界情況很難模擬出來然后測試倦挂。
代碼如下畸颅,實(shí)現(xiàn)不完整:
https://github.com/theoneLee/go-raft-demo

todo

后面發(fā)現(xiàn)raft.github.io上有一個(gè)mit分布式系統(tǒng)的課程,里面包含了raft的一個(gè)實(shí)現(xiàn)方援,且附帶完善的測試用例没炒,準(zhǔn)備好好看課程,然后實(shí)現(xiàn)這塊內(nèi)容。

參考

  • 論文

https://ramcloud.atlassian.net/wiki/download/attachments/6586375/raft.pdf

  • 可視化raft

https://raft.github.io/

http://thesecretlivesofdata.com/raft/

  • mit 6.824

http://nil.csail.mit.edu/6.824/2020/index.html

  • 《云原生分布式存儲(chǔ)基石 etcd深入解析》第一章
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末犯戏,一起剝皮案震驚了整個(gè)濱河市送火,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌先匪,老刑警劉巖种吸,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異呀非,居然都是意外死亡坚俗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門岸裙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坦冠,“玉大人,你說我怎么就攤上這事哥桥≌藁耄” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵拟糕,是天一觀的道長判呕。 經(jīng)常有香客問我倦踢,道長,這世上最難降的妖魔是什么侠草? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任辱挥,我火速辦了婚禮,結(jié)果婚禮上边涕,老公的妹妹穿的比我還像新娘晤碘。我一直安慰自己,他們只是感情好功蜓,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布园爷。 她就那樣靜靜地躺著,像睡著了一般式撼。 火紅的嫁衣襯著肌膚如雪童社。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天著隆,我揣著相機(jī)與錄音扰楼,去河邊找鬼。 笑死美浦,一個(gè)胖子當(dāng)著我的面吹牛弦赖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播浦辨,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蹬竖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了荤牍?” 一聲冷哼從身側(cè)響起案腺,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎康吵,沒想到半個(gè)月后劈榨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晦嵌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年同辣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惭载。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旱函,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出描滔,到底是詐尸還是另有隱情棒妨,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布含长,位于F島的核電站券腔,受9級特大地震影響伏穆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纷纫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一枕扫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辱魁,春花似錦烟瞧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至剖笙,卻和暖如春卵洗,著一層夾襖步出監(jiān)牢的瞬間请唱,已是汗流浹背弥咪。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留十绑,地道東北人聚至。 一個(gè)月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像本橙,于是被迫代替她去往敵國和親扳躬。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359