用一张白纸推导出 RAFT 算法
作者:nolanyang,騰訊 PCG 后臺(tái)開(kāi)發(fā)工程師
分布式共識(shí)算法中,raft 主打的就是易理解性,近年來(lái)隨著業(yè)界對(duì)算法的開(kāi)發(fā),raft 算法的應(yīng)用也被大大拓展。raft 的論文,先描述了算法協(xié)議,再用反證法為主的多種證明方法證明其正確性,并沒(méi)有給出完整的算法推導(dǎo)過(guò)程,因此很多同學(xué)多次閱讀論文后,對(duì)算法的掌握心里還是沒(méi)什么底,而閉著眼睛用組件總是有點(diǎn)發(fā)虛的。本文從一個(gè)故事開(kāi)始,給大家模擬推導(dǎo)一遍 raft 協(xié)議中的核心規(guī)則是怎么構(gòu)建出來(lái)的,希望能讓更多的同學(xué)有動(dòng)力去理解和應(yīng)用分布式共識(shí)算法。感謝 raft 論文翻譯項(xiàng)目為我們高效閱讀論文提供了很大幫助。項(xiàng)目地址:https://github.com/maemual/raft-zh_cn
一、故事緣起
故事要從一次具有歷史意義的時(shí)空之旅開(kāi)始,5 個(gè)家學(xué)淵源的家庭,以更準(zhǔn)確的記錄下 3000 多年的文明興衰史為目標(biāo),穿越到華夏文明的起點(diǎn),他們要各自建立起以自己家族為中心的村落,通過(guò)自己的歷史小村一代一代將發(fā)生的歷史事件記錄并傳承下去。出發(fā)前 5 個(gè)歷史小村的第一代村長(zhǎng)進(jìn)行了一次擬定這個(gè)宏偉計(jì)劃的會(huì)議。會(huì)前他們先隨手在白紙上貼上了一張古代地圖,標(biāo)注了自己村子的起始地點(diǎn)。白紙如圖:
圖1 5村起始位置(圖片來(lái)源于網(wǎng)絡(luò))
準(zhǔn)確記錄歷史在很多時(shí)代都是一件危險(xiǎn)的事情,這也是為什么 5 個(gè)村莊需要分散在天南地北的主要原因,司馬村長(zhǎng)提出了他們的第一個(gè)目標(biāo),任何時(shí)代只要還有三個(gè)村落能正常運(yùn)作,就要能夠準(zhǔn)確一致的記錄下這個(gè)時(shí)代的事情。5 個(gè)村長(zhǎng)彼此間都無(wú)比信任,也堅(jiān)信他們各自的歷史小村無(wú)論何時(shí)都不會(huì)在規(guī)則之外竄改記錄下來(lái)的信息。
記錄歷史事件自然需要紙張什么的,遠(yuǎn)古時(shí)代編年歷法不一定統(tǒng)一,各村落通信周期也不穩(wěn)定,村長(zhǎng)們很快商量出了一個(gè)方案,統(tǒng)一使用編好頁(yè)碼的記事本進(jìn)行記錄,1 件事情記錄在 1 頁(yè)上,這樣至少事情的先后順序不會(huì)輕易搞錯(cuò)了。然后左村長(zhǎng)提出一個(gè)關(guān)鍵問(wèn)題,紙質(zhì)材料和油墨雖然修改方便,但是不易保存,就算在歷史長(zhǎng)河中不被損毀,千年過(guò)去最早的記事本也沒(méi)法再看了,根據(jù)他看過(guò)一部科幻小說(shuō),左村長(zhǎng)提出,記事本上的事情只要村落之間“達(dá)成一致”了,就可以把事情按順序刻在石碑上埋到地底下,這就是人類(lèi)目前最有效的保存信息的方法了,這樣做的缺點(diǎn)就是刻在石碑的內(nèi)容就不好更改了。大家紛紛表示同意,司馬村長(zhǎng)把這些信息記錄在他的大白紙上:
圖2 記錄歷史的主要工具:記事本和石碑
二、前提條件的制定
講完這些,大家都把注意力集中到了剛才左村長(zhǎng)無(wú)意間提出來(lái)的“達(dá)成一致”這四個(gè)關(guān)鍵字上。班村長(zhǎng)首先提出來(lái),如果每個(gè)時(shí)期只有一個(gè)村落負(fù)責(zé)把發(fā)生的事件記錄下來(lái),而且保持所有村落都是 1 頁(yè)上面只記錄 1 個(gè)事件,其他村落只負(fù)責(zé)接收這個(gè)村落的消息,把接收到的消息記錄在記事本對(duì)應(yīng)的頁(yè)碼上,那記事本上每一頁(yè)上的事件保持一致的難度就小很多了。如果多個(gè)村落同時(shí)記錄歷史事件,然后通過(guò)互通消息來(lái)協(xié)調(diào)一致,難度就會(huì)大很多,處理每一頁(yè)上的記錄沖突也會(huì)需要很多時(shí)間。
大家一商量,雖然有一點(diǎn)爭(zhēng)議,但最終還是認(rèn)同了這個(gè)提議,多點(diǎn)記錄確實(shí)比較難,最終效率也不一定高。左村長(zhǎng)接著就拋出了兩個(gè)名詞作為約定,“主家”即當(dāng)前負(fù)責(zé)記錄的村子,“分家”即當(dāng)前接收主家消息的村子。左村就主動(dòng)承擔(dān)第一期的主家任務(wù)了。接下來(lái)就是司馬村長(zhǎng)發(fā)話(huà)了,前面他也說(shuō)過(guò),目標(biāo)是任何時(shí)代即使有兩個(gè)村落被滅,記錄也要準(zhǔn)確的傳下去,左村長(zhǎng)的方案里面,每個(gè)時(shí)代負(fù)責(zé)記錄的村子發(fā)生意外的時(shí)候,如果他們記在石碑上的事件,別的村子還沒(méi)收到,那記錄的事件就會(huì)出現(xiàn)不一致了,以后之前被滅村子的石碑被后人挖出來(lái)以后,歷史事件可能出現(xiàn)矛盾,當(dāng)然這還是最簡(jiǎn)單的情況了,更復(fù)雜的這一類(lèi)情況還能一口氣說(shuō)出很多。
大家開(kāi)始繼續(xù)思考這個(gè)問(wèn)題,顧村長(zhǎng)第一個(gè)發(fā)表觀點(diǎn),一件事情只要有 3 個(gè)村落記錄下來(lái),那之后任何時(shí)刻失去任意兩個(gè)村子,這件事情都不會(huì)丟,因?yàn)樽畈钜彩沁€有一個(gè)村落記下了這件事。所以說(shuō)一件事情只要有 3 個(gè)村落在記事本上記下來(lái)了,那這件事情就可以被放心的刻到石碑上了,一個(gè)事件確認(rèn)可以被記錄在石碑上了,就稱(chēng)這個(gè)事件安全了。至于誰(shuí)來(lái)統(tǒng)計(jì)有幾個(gè)村莊記錄了某一件事情,那自然是主家了。左村長(zhǎng)的方案里面只有主家負(fù)責(zé)發(fā)出消息,其他從家都只能在主家發(fā)來(lái)消息以后做相應(yīng)記錄,再讓信使把記錄的情況發(fā)回給主家,主家根據(jù)回來(lái)的信使的消息自然可以統(tǒng)計(jì)有多少村落記記錄下了這個(gè)事件了,主家在后面的通信中再把哪些事件可以刻到石碑上了,帶給各村,那就很穩(wěn)妥了,所以主家要統(tǒng)計(jì)一份各村已經(jīng)記錄下的事件列表。
司馬村長(zhǎng)一聽(tīng)立刻就說(shuō)這還不夠:要是左村長(zhǎng)記錄下來(lái)的第一頁(yè)上的事情,簡(jiǎn)稱(chēng)事件 1,同步給了顧村和歐陽(yáng)村,但沒(méi)傳給另外兩個(gè)村子,然后左村記錄的事件 2,又只同步給了司馬村和班村,這時(shí)候左村長(zhǎng)是可以大大方方刻石碑了,但要是刻完石碑就被滅村,那剩下 4 家選誰(shuí)做主家,都沒(méi)有兩件事情完整的記錄,要把剩下 4 家手上的記錄合起來(lái)又要非常復(fù)雜的通信規(guī)則了。情況就像圖上這樣(填了藍(lán)色代表這頁(yè)記錄了事件):
圖3 可能出現(xiàn)的記事本有空頁(yè)的情況
左村長(zhǎng)這時(shí)也提出一個(gè)問(wèn)題,就算主家一直沒(méi)出什么問(wèn)題,過(guò)了一千年,那主家記錄各個(gè)事件在各村的記錄情況的列表也是長(zhǎng)的看不懂了,給各家補(bǔ)齊少記錄的事件也是不知從何下手啊。一時(shí)間與會(huì)眾人難以定出個(gè)結(jié)論來(lái),這時(shí)候歐陽(yáng)村長(zhǎng)終于發(fā)言了:太復(fù)雜的事情不好分析,我們就簡(jiǎn)單點(diǎn)處理吧,每次同步事件的時(shí)候,有 3 個(gè)以上村落都有著從開(kāi)始到當(dāng)前頁(yè)碼都相同的記錄時(shí),才算安全,才可以把這件事刻到石碑上,這樣任意 2 個(gè)村被滅,都會(huì)至少有一個(gè)村保留所有可以被刻到石碑上的事件,下一輪只要能把滿(mǎn)足這樣條件的村莊選出來(lái)做主家,那這個(gè)新的主家就能帶領(lǐng)剩下的分家把之前的記錄傳承下去了。
另外這樣做了以后,任何分家接收到主家傳來(lái)的消息,如果發(fā)現(xiàn)前面有缺漏的事件,那也不用再跳過(guò)前面的頁(yè)碼來(lái)記錄當(dāng)前事件了,因?yàn)橛浵聛?lái)了也不能作為當(dāng)前事件的確認(rèn)方被統(tǒng)計(jì)計(jì)數(shù),也就是說(shuō)記事本中間再也不會(huì)有空洞了,而主家也不用保留超級(jí)復(fù)雜的統(tǒng)計(jì)列表了,只要記一下各個(gè)分家最后記錄下來(lái)的事情是第幾頁(yè)的事情就能掌握全局情況。其實(shí)這時(shí)候還得出了一個(gè)階段性成果:因?yàn)橛涗浭录臅r(shí)候,要確認(rèn)前面的事件是不是都一樣,只有都一樣才能記錄下來(lái),只要一個(gè)時(shí)期只有一個(gè)村莊做主家,那么兩個(gè)和主家正常通信的分家的記事本上,只要在任意一頁(yè)上“相同”,那這頁(yè)之前的內(nèi)容肯定是“相同”的了,任意分家和主家之間也滿(mǎn)足這個(gè)結(jié)論。
這一下大家都開(kāi)心了,這個(gè)辦法簡(jiǎn)單易懂,司馬村長(zhǎng)立刻把剛才討論的結(jié)果記錄到大白紙上:(到這里大家討論出來(lái)的兩個(gè)最關(guān)鍵的前提條件:只有一個(gè)主家負(fù)責(zé)記錄事件,其他分家只負(fù)責(zé)接收主家的消息;記事本不能有空洞。這兩個(gè)前提條件并不是靠推理得出的,而是大家希望簡(jiǎn)化后面的規(guī)則提出的假設(shè),如果沒(méi)有這兩個(gè)條件,那大家后面可能會(huì)推導(dǎo)出別的分布式共識(shí)算法哦)
圖4 最初提出的復(fù)制事件的規(guī)則
三、解釋解釋什么叫做“相同”
但是很快大家就發(fā)現(xiàn)了新的問(wèn)題,確認(rèn)過(guò)往所有的記錄事件是不是都“相同”,這可不容易做啊,先不說(shuō)確認(rèn)“相同”是不是就是比較記錄的每一個(gè)字都一樣,只要持續(xù)記錄很多年,比較一次也要花上太多時(shí)間了吧,每次有新的事件都要從頭開(kāi)始比較,這誰(shuí)受得了?司馬村長(zhǎng)立刻發(fā)表了高論:用我小學(xué)二年級(jí)學(xué)過(guò)的最簡(jiǎn)單的數(shù)學(xué)歸納法就可以破這個(gè)問(wèn)題了,對(duì)于在區(qū)間(0,N]的任意 n, 都滿(mǎn)足元素 S1(n)== S2(n)且 S2(n-1)== S1(n-1),那么在[0,N]區(qū)間內(nèi),S1 和 S2 這兩個(gè)數(shù)組每個(gè)位置上的元素都是相等的。
在我們這個(gè)問(wèn)題里,說(shuō)人話(huà)就是每一個(gè)新事件記錄的時(shí)候,主家在同步消息的時(shí)候除了當(dāng)前事件還要帶上前一頁(yè)記錄的事件,收到消息的分家要比較一下前一頁(yè)上的事件和自己記錄的是不是“相同”,只有前一頁(yè)的事件“相同”才記錄新一頁(yè)的事件,只要從頭一開(kāi)始就這么做,那么每次能順利記錄下新事件的村落,前面記錄的事件和主家肯定是都“相同”的。大家紛紛拍案叫絕,只有顧村長(zhǎng)問(wèn)了一聲,要是分家接到消息的時(shí)候,發(fā)現(xiàn)主家傳來(lái)的前一頁(yè)事件和自己這里記錄的不一樣或者根本還沒(méi)記錄過(guò)這一頁(yè)的事情,那可怎么辦呢?
歐陽(yáng)村長(zhǎng)立刻發(fā)話(huà),還是用簡(jiǎn)單的方法,上面的情況,分家直接回復(fù)主家現(xiàn)在不能記錄這一頁(yè)上的事件,主家接到失敗的消息就把前一頁(yè)的事件作為這個(gè)分家下次要同步的事件在下次發(fā)消息的時(shí)候發(fā)前一頁(yè)的事件,而下次發(fā)消息附帶的也就是前前頁(yè)的事件了,要是這次同步能成功,那就繼續(xù)嘗試同步新的事件,反之,就以此類(lèi)推,繼續(xù)往前嘗試更靠前的頁(yè)碼上的事情,主家再單獨(dú)保存一份要給各分家下次嘗試同步哪一頁(yè)的列表就能輕松做好這個(gè)事情了。這一波討論完,大家一下子都安心了,最重要的主家怎么把自己記錄下來(lái)的事件復(fù)制給分家的主要機(jī)制都討論出來(lái)了,司馬村長(zhǎng)開(kāi)心的在白紙上開(kāi)始了記錄。他先畫(huà)了一張草稿說(shuō)明以后主家怎么同步消息給分家:
圖5 主家發(fā)送同步事件的基本規(guī)則
司馬村長(zhǎng)剛畫(huà)完示意圖,左村長(zhǎng)就發(fā)現(xiàn)了問(wèn)題:就算只比較前一頁(yè)的事件一樣不一樣,也沒(méi)有百分百的把握啊,遠(yuǎn)古時(shí)期年歷容易出現(xiàn)偏差,地名也不一定對(duì)齊,傳遞消息時(shí)間又長(zhǎng),萬(wàn)一不同的地方連續(xù)出現(xiàn)類(lèi)似的天災(zāi)一類(lèi)的事件,說(shuō)不定記錄都差不多,前面反復(fù)說(shuō)到的事件“相同”的這個(gè)“相同”要有明確的判斷標(biāo)準(zhǔn)才行啊。
大家再次陷入沉思,大概五分鐘之后,又是歐陽(yáng)村長(zhǎng)說(shuō)話(huà)了:要簡(jiǎn)單判斷事件是不是“相同”,要把我們之前一個(gè)沒(méi)用上的關(guān)鍵信息給用起來(lái)了。按照我們之前的設(shè)定,理想情況下一個(gè)時(shí)期只會(huì)有一個(gè)主家負(fù)責(zé)記錄事件,如果主家出事了,分家都收不到主家的信息了,可能選出一個(gè)新的村落做主家,每一個(gè)主家上任后負(fù)責(zé)記錄的這段時(shí)間就叫主家的任期好了,我們給每一個(gè)任期遍一個(gè)號(hào),叫任期號(hào),任期號(hào)從 1 開(kāi)始,每次新上任的主家的任期號(hào)就是上一個(gè)任期號(hào)加 1,這樣每段任期就有唯一的任期號(hào)了。
每一個(gè)主家記錄事件的時(shí)候,給每個(gè)事件都記上自己當(dāng)時(shí)的任期號(hào)并在同步事件的時(shí)候都帶著事件對(duì)應(yīng)的任期號(hào),分家收到事件的時(shí)候,記錄的時(shí)候也把對(duì)應(yīng)的任期號(hào)記錄在那一頁(yè)上,這樣下來(lái),只要所有主家保證不修改自己之前記錄下的任何一頁(yè)上的事件,那可以簡(jiǎn)單推導(dǎo)出來(lái):同一頁(yè)上標(biāo)注的任期號(hào)相同,這一頁(yè)的記錄就一定相同。這時(shí)候顧村長(zhǎng)表示這通操作不太理解了,這時(shí)不怎么說(shuō)話(huà)的班村長(zhǎng)拿起記事本給他演示了一下:
圖6 記事本每一頁(yè)記錄事件的樣式
正常情況下任何時(shí)期我們里面的主家只有一個(gè),對(duì)應(yīng)的任期號(hào) T 也只有一個(gè),主家在第 N 頁(yè)上記下事件的時(shí)候帶上了自己的任期號(hào) T,相當(dāng)于告知大家這頁(yè)的事件是我寫(xiě)的,只要主家保證自己記錄下來(lái)的事件,自己在這一個(gè)任期內(nèi)絕對(duì)不會(huì)改,那分發(fā)出去的任何一頁(yè)上的事件,只要任期號(hào)相同就代表是同一個(gè)主家在同一個(gè)任期記錄下來(lái)的,這個(gè)主家在這個(gè)任期里又絕不可能回去改這個(gè)事件,那這頁(yè)上的事件也就是相同的了。
所以我們以后表示一個(gè)事件只要用 S(N,T)這種形式就可以了,頁(yè)碼 N 和任期 T 都相等,事件一定“相同”,以后這就是事件“相同”的定義了,事件相同也就再也不用打引號(hào)了。順帶一提,主家每次同步過(guò)來(lái)的事件里的任期號(hào),分家也要找個(gè)地方記下來(lái),這樣下次分家被選為主家的時(shí)候,才能正確的把任期加 1 哦。說(shuō)到這里,大家都懂了,主家向分家同步記錄下來(lái)的事件的步驟都差不多出來(lái)了,司馬村長(zhǎng)也飛快的完成了這階段的記錄。(圖片請(qǐng)雙擊放大瀏覽)
圖7 初步完成了復(fù)制事件機(jī)制的設(shè)計(jì)
司馬村長(zhǎng)記錄的同時(shí)還補(bǔ)上了主家怎么確認(rèn)一頁(yè)上記錄的事件是不是可以被安全送去刻到石碑上的相關(guān)操作規(guī)則,還把主家已經(jīng)確認(rèn)的最大安全頁(yè)碼加在了主家傳給分家的消息里,這樣分家也能結(jié)合自己的情況確定自己可以刻到石板上的最大頁(yè)碼了。為了方便刻石碑,還規(guī)定了每個(gè)村落把已經(jīng)刻好的最大頁(yè)碼以及可以安全刻下來(lái)的最大頁(yè)碼分別記錄一下。
最后他還提出來(lái),可能以后有些人會(huì)向村落爆料一些事件,如果這時(shí)候這個(gè)村落不是主家,又不知道主家現(xiàn)在是誰(shuí),那還挺尷尬的,所以每次主家傳消息的時(shí)候記得報(bào)一下自己是誰(shuí),這樣就可以引導(dǎo)爆料群眾直接去主家那里直接爆料了。這些設(shè)定都很容易理解,大家紛紛直呼內(nèi)行。不過(guò)班村長(zhǎng)還是指出了筆記上幾處語(yǔ)句不通順以及字寫(xiě)錯(cuò)的地方。
四、選舉爭(zhēng)霸
討論到這里,大家都挺累的了,司馬村長(zhǎng)和歐陽(yáng)村長(zhǎng)相視一笑,有了這些設(shè)定,是時(shí)候短兵相接,快速討論一下選舉主家的規(guī)則了吧。
司馬:穿越過(guò)去以后,要是我們一段時(shí)間沒(méi)收到左村長(zhǎng)的消息,我們是不是就可以開(kāi)始選新的主家了,但是萬(wàn)一這段時(shí)間其實(shí)是左村長(zhǎng)覺(jué)得沒(méi)發(fā)生什么值得記錄的事件,那就不太好了,我們到時(shí)候搞出兩個(gè)主家來(lái),前面的規(guī)則都破了。
歐陽(yáng):簡(jiǎn)單處理,主家的人,就算沒(méi)有要記錄的事件發(fā)生,也最少每個(gè)月給所有分家發(fā)個(gè)消息,就算消息里面沒(méi)有要記錄的事件也要發(fā),算是報(bào)個(gè)平安吧。哪個(gè)分家連續(xù)三個(gè)月沒(méi)收到主家的消息,再發(fā)起選舉不遲。
司馬:發(fā)起選舉是容易,我們收到選舉要按什么規(guī)則投票呢?按照前面的思路,我們可是要選出一個(gè)擁有前任主家進(jìn)行過(guò)安全確認(rèn)的所有事件的村落出來(lái)傳承的哦。
歐陽(yáng):簡(jiǎn)單處理,任期號(hào)代表時(shí)代只增不減,記事本上有記錄事件的最大頁(yè)碼上的任期號(hào)誰(shuí)大,誰(shuí)的記事本就內(nèi)容更全,如果任期號(hào)一樣大,那最大頁(yè)碼大的記事本就更全,要是頁(yè)碼和任期號(hào)都一樣,那記事本就一樣全。每個(gè)村莊只投票給記事本最少和自己一樣全的村莊不就可以了么。
司馬:那得了幾票,就可以做主家了呢?
歐陽(yáng):簡(jiǎn)單處理,就是包括自己在內(nèi)的 3 票啊,前面我們?cè)O(shè)定過(guò)了,最后一個(gè)被確認(rèn)安全的事件,包括主家最少有 3 個(gè)村莊已經(jīng)記下來(lái)了。記錄下最新事件的村子就是有最全內(nèi)容的村子,包括主家在內(nèi)有 3 個(gè)這樣的村子。就算剩余其他 2 個(gè)村莊還沒(méi)來(lái)得及記下最新的事件的時(shí)候,主家被滅了,記下最新事件的 2 個(gè)村莊里面不管誰(shuí)參與選舉,誰(shuí)就能得到包括自己在內(nèi)的 4 票,反之,沒(méi)記下最新事件的 2 個(gè)村子參與選舉,最多只能得 2 票。我們?cè)O(shè)定的最壞情況也就是同時(shí)再被滅掉一個(gè)記錄事件最全的村子,那這時(shí)候剩下唯一一個(gè)記錄最全的村子還是能夠得到包括自己在內(nèi)的 3 票支持,成功當(dāng)選新的主家。
總結(jié)起來(lái)說(shuō)就是不管被滅幾家,只要能贏得 3 票,上一個(gè)任期的主家能確認(rèn)安全的事件,在新選出來(lái)的主家記事本里面肯定有。我圖都給你畫(huà)出來(lái)了:
圖8 記錄“最新”事件的村莊能贏得競(jìng)選
司馬:不對(duì)啊,你的圖里面,我只要拿到你和班村的票就能做主家了,萬(wàn)一顧村沒(méi)給我投票,而是也發(fā)起了選舉,他也能贏得你和班村的選票,也是 3 票也能成為主家,那不就 2 個(gè)主家了嗎?
歐陽(yáng):這個(gè)問(wèn)題連簡(jiǎn)單處理都不需要了,你們發(fā)起選舉的時(shí)候,先把自己的任期號(hào)增加一下,變成自己當(dāng)選以后要用的任期號(hào)發(fā)出去,每個(gè)村子在一個(gè)任期號(hào)里最多投一次票,記錄下來(lái)每個(gè)任期號(hào)是投票給了哪個(gè)村子。這樣你拿到 3 票時(shí)候,顧村長(zhǎng)再開(kāi)始發(fā)起投票,他的任期號(hào)最多和你一樣,他是拿不到其他已經(jīng)投過(guò)票的村子的支持的。除非再過(guò)三個(gè)月,期間顧村長(zhǎng)都沒(méi)收到過(guò)你同步的任何消息,他再次增加任期號(hào)發(fā)起選舉才有機(jī)會(huì)再當(dāng)主家。而如果這三個(gè)月里面,你真的什么事都沒(méi)記下來(lái),那顧村才可能當(dāng)選,但是任期號(hào)也增加了,他用的還是唯一任期號(hào),我們的機(jī)制也繼續(xù)維持著,沒(méi)什么影響。
司馬:還有問(wèn)題啊,你剛才說(shuō)的是我先拿到 3 票的情況,問(wèn)題是我和顧村可能是同時(shí)發(fā)起選舉的,萬(wàn)一我們一個(gè)得到了你的支持,一個(gè)得到了班村的支持,那我們豈不是這一輪不分勝負(fù),到下一輪我們?cè)偻瑫r(shí)發(fā)起,又是各得兩票,豈不是沒(méi)完沒(méi)了,我們時(shí)間都花在選舉上了,怎么好好記錄歷史啊。
歐陽(yáng):這個(gè)也是可以簡(jiǎn)單處理的,我們每家每次發(fā)起選舉的時(shí)間不要那么固定嘛。比如說(shuō)你有時(shí)候是 3 個(gè)月零 7 天收不到任何消息發(fā)起選舉,有時(shí)候是 4 個(gè)月發(fā)起選舉,顧村他們有時(shí)候是 3 個(gè)月零 14 天收不到消息發(fā)起選舉,有時(shí)候是 4 個(gè)月零 7 天發(fā)起選舉,你們發(fā)起選舉沖突的概率不就低了?
左村長(zhǎng):你們別太過(guò)分了,萬(wàn)一上面那個(gè)情況里面,我們村不是被滅了,只是信使都被攔截了,我可是一直都在辛苦的記錄著各種事件,只是沒(méi)法同步給你們,我自己也沒(méi)法確認(rèn)新記錄的事件是不是可以被刻到石碑上。那三年后我龍王歸來(lái),哦不是,我找回信使,把我這隱忍三年記錄下來(lái)的事件傳遞給你們,你們認(rèn)還是不認(rèn)?
歐陽(yáng):簡(jiǎn)單起見(jiàn),不管你是修羅還是什么神醫(yī),我們都不認(rèn),因?yàn)槟愕膬?nèi)容合進(jìn)來(lái),我們還要要保持一致太復(fù)雜了,而且你那三年記下來(lái)的內(nèi)容又沒(méi)被安全確認(rèn)過(guò),你也不敢刻在石碑上,你回來(lái)以后乖乖接收我們補(bǔ)給你的安全的事件,去刻你的石碑就完事了。哪一頁(yè)開(kāi)始,你記錄事件的任期號(hào)和新主家不一樣了,你就從這一頁(yè)開(kāi)始把后面的內(nèi)容都劃掉啊。
左村長(zhǎng):我 TM 什么時(shí)候說(shuō)過(guò)修羅、神醫(yī)什么的了?而且我們之前定的規(guī)則里面,也沒(méi)說(shuō)我這個(gè)歸來(lái)龍王,哦不,我這個(gè)歸來(lái)主家不能繼續(xù)同步記錄的事件給你們啊!
歐陽(yáng):簡(jiǎn)單處理,我補(bǔ)個(gè)通用規(guī)則,反正同步事件和發(fā)起選舉兩種消息里面都帶有任期號(hào)這個(gè)信息了,任期號(hào)只增不減,更大的任期號(hào)代表更新的時(shí)代,所以我宣布收到的消息里面,任期號(hào)小于自己之前記錄下來(lái)的,一律拒絕,任期號(hào)大于自己之前的,一律把自己記錄的任期號(hào)更新為接收到的任期號(hào),然后再根據(jù)之前的設(shè)定操作,誰(shuí)贊成,誰(shuí)反對(duì)?
左村長(zhǎng):我之前是主家,我收到你們的消息里面的任期號(hào)比我自己的大,我也要聽(tīng)你們的嗎?
歐陽(yáng):簡(jiǎn)單處理啊,不管你是主家,還是你發(fā)起選舉做了候選人,收到的消息里面的任期號(hào)比你們自己的大,就自覺(jué)一點(diǎn),主動(dòng)把自己村的地位降到分家,不就可以了嗎?因?yàn)槟銈兌煎e(cuò)過(guò)了至少一個(gè)時(shí)代了,還想繼續(xù)做主家,別人豈不是都要配合你們,這時(shí)候說(shuō)不定別人好多你們錯(cuò)過(guò)的事件都刻到石碑上了。順便說(shuō)一下啊,剛才我已經(jīng)設(shè)定了每家等待個(gè)一定范圍內(nèi)的隨機(jī)時(shí)間再發(fā)起選舉的規(guī)則,選舉沖突不容易發(fā)生了,我們剛穿越回去那會(huì)兒,也不用提前指定誰(shuí)做第一個(gè)主家了,也用這種選舉機(jī)制來(lái)選吧,這樣左老師你壓力也不用那么大了哈。
左村長(zhǎng):。。。
大家停下來(lái)消化了一下前面對(duì)話(huà)的內(nèi)容,發(fā)覺(jué)歐陽(yáng)村長(zhǎng)說(shuō)的這一套還真是能解決很多問(wèn)題,司馬村長(zhǎng)開(kāi)始整理筆記了。
圖9 根據(jù)針對(duì)選舉的討論完善出來(lái)的全套機(jī)制
這次因?yàn)樾略龅膬?nèi)容比較多了,司馬村長(zhǎng)把新增的內(nèi)容都用紅色筆來(lái)記了,還修復(fù)一些班村長(zhǎng)之前吐槽的不通順的語(yǔ)句,還補(bǔ)充了投票過(guò)程中容易想出來(lái)的一些細(xì)節(jié)。寫(xiě)完以后大家看了一會(huì),司馬村長(zhǎng)覺(jué)得十分滿(mǎn)意了,最后達(dá)成的成果(5)就是大家的終極目標(biāo)了,但是這時(shí)候顧村長(zhǎng)又看出問(wèn)題來(lái)了。
顧村長(zhǎng):前面那個(gè)圖 8 里面,要是通信方式全部被卡斷的,不是作為主家的左村,而是作為分家的我們顧村的話(huà),就有問(wèn)題了。我們顧村收不到任何消息,3 個(gè)月以后自己成為候選村,增大自己的任期號(hào),發(fā)出去的選舉消息你們也收不到,然后我們顧村繼續(xù)提升自己的任期號(hào),不斷發(fā)起新的選舉。要是三年后,通信恢復(fù)了,我的任期號(hào)最大,你們收到我的選舉請(qǐng)求,都要參見(jiàn)龍王了。
歐陽(yáng):三年后我們收到你的選舉消息,確實(shí)主家也要自認(rèn)任期號(hào)不夠看,自降為分家,但只要這三年里有新事件被主家安全確認(rèn)過(guò),你也不可能被投成新的主家。因?yàn)槲覀兺镀边€要看你最后的記錄事件有沒(méi)有我們的新。你自己這三年記錄不了任何事件,主家只要有新事件安全確認(rèn)過(guò),那至少有 3 家記錄了這個(gè)新事件,這 3 家都不會(huì)投票給你,你最多 2 票,怎么當(dāng)選主家呢?所以說(shuō),最后當(dāng)主家的,一定還是記下了所有事件的村子。反過(guò)來(lái)說(shuō),要是三年里面沒(méi)新事件被安全確認(rèn),那你當(dāng)上主家,也沒(méi)什么問(wèn)題,因?yàn)槟阋彩怯涗浟怂惺录?#xff0c;你當(dāng)主家就當(dāng)唄。
當(dāng)然了,你這樣冒出來(lái),打斷本來(lái)干的好好的主家的工作,確實(shí)影響了大家對(duì)歷史的記錄,投票這段時(shí)間,都沒(méi)有主家正常工作了,所以這點(diǎn)我們可以補(bǔ)一些限制規(guī)則,比如正式投票前讓你發(fā)個(gè)民意調(diào)查什么的,要是你民意調(diào)查都過(guò)不了,就不要發(fā)什么選舉來(lái)浪費(fèi)大家時(shí)間了,當(dāng)然這個(gè)規(guī)則可以以后補(bǔ)充清楚,因?yàn)槊褚庹{(diào)查也可能拖慢了正常需要發(fā)起選舉的節(jié)奏。
五、龍王歸來(lái)
歐陽(yáng)村長(zhǎng)說(shuō)完以后,大家都很佩服其深謀遠(yuǎn)慮,這時(shí)候班村長(zhǎng)拿出來(lái)一份打印材料,大家都瞥了一眼,居然是洋文的,標(biāo)題很長(zhǎng):“In Search of an Understandable Consensus Algorithm”。然后班村長(zhǎng)發(fā)話(huà)了,今天晚上大家能制定出這樣一個(gè)計(jì)劃來(lái),很了不起了,不過(guò)還有兩個(gè)點(diǎn)大家是沒(méi)想到的,一個(gè)是主家新上任的時(shí)候,主家已經(jīng)確認(rèn)安全的最大頁(yè)碼,不一定比分家都大,傳遞記錄事件消息的時(shí)候,要特別注意,司馬你記得筆記里復(fù)制規(guī)則那里其實(shí)是憑直覺(jué)蒙對(duì)的。這一點(diǎn)我提示你們以后,你們仔細(xì)想想,應(yīng)該都能想到的。不過(guò)還有一種情況就不容易想到啦,你們過(guò)來(lái)看看這張圖:
圖10 超級(jí)復(fù)雜的輪流做主家(論文原文圖8)
班村長(zhǎng)開(kāi)始解釋了,我這個(gè)圖上不同顏色的小方塊代表不同任期記錄下來(lái)的事件,小方塊里的數(shù)字代表事件記錄里的任期號(hào)是多少,abcd(e)代表順序的4 個(gè)時(shí)間點(diǎn),每個(gè)時(shí)間點(diǎn)上,誰(shuí)的記錄外面的框輪廓粗,誰(shuí)就是當(dāng)時(shí)的主家,比如(a)時(shí)期 S1 就是主家,(d)時(shí)期 S5 就是主家。我們來(lái)看一下發(fā)生了什么啊,(a)這個(gè)時(shí)間點(diǎn)呢,S1 是主家,把他記錄的第 2 頁(yè)上的事件(黃色)同步給了 S2,然后他們村連同還在路上的信使都被滅了。
到了(b)這個(gè)時(shí)間呢,S5 因?yàn)樵谶x舉中,得到 S3,S4 的支持當(dāng)上了主家,并且他在第 2 頁(yè)上記錄了一個(gè)新事件(藍(lán)色)。這里都好理解吧,S5 贏得選舉的時(shí)候,因?yàn)橹挥?S2 一個(gè)村有更新的事件,所以只要 S5 先發(fā)起選舉,S5 就可以得到 3 票了,反正黃色事件之前也不可能被任何村子確認(rèn)為安全,所以這個(gè)結(jié)果也合理嘛。
到了(c)時(shí)期,S5 這個(gè)村子連同信使都被滅了,他們記錄下來(lái)的事件一個(gè)村子都沒(méi)傳遞到,而 S1 那邊出現(xiàn)了幸存者,憑著記錄的黃色事件,在連續(xù)將任期號(hào)提升到 4 以后,贏得了選舉,并且在第 3 頁(yè)上記錄下來(lái)了新的事件(粉色)。在將新事件傳遞給 S3 的時(shí)候,因?yàn)楸痪芙^所以退而求其次將將第 2 頁(yè)上的黃色事件同步給了 S3,這樣 S3 在第 2 頁(yè)上也記錄下了黃色事件,黃色事件也就有 3 個(gè)村子記錄下來(lái)了,按照你們前面的討論,S1 就要對(duì)黃色事件做安全確認(rèn)了。
要是 S1 確認(rèn)黃色事件安全,將其刻到石碑上,之后再次被滅村,S5 那邊的幸存者,憑借自己在第二頁(yè)上記錄下來(lái)的藍(lán)色事件,至少可以在連續(xù)提升自己任期號(hào)到 5 以后,在選舉中得到 S3 和 S4 的支持。因?yàn)?S3 雖然記錄了黃色事件,但黃色事件的任期號(hào)是 2,藍(lán)色事件的任期號(hào)是 3,藍(lán)色事件在規(guī)則中是更新的。S5 贏得選舉做了主家以后,在(d)這個(gè)時(shí)候,是可以把第 2 頁(yè)上的藍(lán)色事件復(fù)制到其他所有村子記事本的第 2 頁(yè)上的,藍(lán)色事件在第 2 頁(yè)上被確認(rèn)安全的話(huà),和之前在第 2 頁(yè)上已經(jīng)被 S1 確認(rèn)甚至被刻到石碑上的黃色事件就沖突了。
所以說(shuō)你們前面討論的規(guī)則里面還是又一個(gè)關(guān)鍵漏洞的。不過(guò)你們也別那么痛苦的一副表情了,你們繼續(xù)看圖吧。如果 S1 沒(méi)有那么快被屠村,沒(méi)有(d)這個(gè)時(shí)刻的事情,而是到了(e)這個(gè)時(shí)間點(diǎn)上,完成了復(fù)制粉色事件給 S2,S3 的任務(wù),這時(shí)候 S1 確認(rèn)粉色事件是安全的并刻到石碑上。之后不管發(fā)生什么事情,S5 的藍(lán)色事件在選舉中都是不如 S1,S2,S3 最后記錄的粉色事件新的(任期號(hào)直接分勝負(fù)),也就再也選不上主家了。
總結(jié)一下呢,就是主家只能確認(rèn)自己當(dāng)前任期內(nèi)的事件是否安全,而不能確認(rèn)之前任何一個(gè)任期內(nèi)的事件是否安全,而根據(jù)你們最早就得出的成果(1),一頁(yè)上的事件被確認(rèn)安全了,那這一頁(yè)之前所有頁(yè)上記錄的事件也就安全了,所以過(guò)往任期中的事件是可以通過(guò)當(dāng)前任期的事件安全性被確認(rèn)的。司馬你的紙給我,我來(lái)修改一下吧。
圖11 集中大家的智慧討論出來(lái)的最終方案
班村長(zhǎng)寫(xiě)完以后繼續(xù)說(shuō)到,我把改動(dòng)都用藍(lán)色標(biāo)記出來(lái)了,復(fù)制記錄事件的規(guī)則里面,司馬剛才是憑直覺(jué)寫(xiě)對(duì)了“要確認(rèn)主家的安全頁(yè)碼大于自己的”這類(lèi)規(guī)則的,所以這個(gè)我也標(biāo)出來(lái)了。最重要的,就是最后達(dá)成的成果,我改了一下,可不是隨便一頁(yè)上的事件有超過(guò) 3 個(gè)村子記錄了,就可以確認(rèn)安全哦。切記切記。我在這里隱忍了三個(gè)小時(shí)了,聽(tīng)你們說(shuō)了這么多,現(xiàn)在我要回去睡覺(jué)了。
剩下的村長(zhǎng)沉思了大半個(gè)小時(shí),然后對(duì)著班村長(zhǎng)離去的方向拱手作揖,“參見(jiàn)龍王”。
六、推廣總結(jié)
到這里呢,這個(gè)穿越時(shí)空的小故事就差不多結(jié)束了。之前看過(guò) raft 論文或者現(xiàn)在再去看的同學(xué),應(yīng)該都可以輕松的把故事里面的名詞和論文中的對(duì)應(yīng)起來(lái)了,多多少少對(duì)論文中一開(kāi)始就出現(xiàn)的協(xié)議規(guī)則是怎么來(lái)的能有一些新的理解了。寫(xiě)這個(gè)故事的時(shí)候,通過(guò)好友之間討論的形式來(lái)構(gòu)造的情節(jié),希望通過(guò)這種方式能讓更多同學(xué)對(duì)分布式共識(shí)算法產(chǎn)生興趣,消除恐懼,在自己業(yè)務(wù)里面合適的場(chǎng)景中用上這種能力來(lái)提升服務(wù)的質(zhì)量,當(dāng)然因?yàn)樗接邢蕹霈F(xiàn)的一些錯(cuò)誤也請(qǐng)大家多多包涵指正。
感謝所有不吝三連支持的同學(xué)。最后把這篇文章送給這些年來(lái)在一線(xiàn)研發(fā)崗位上一起奮斗戰(zhàn)友,過(guò)去和你們每一次為了更完美的解決方案的討論,都讓我受益良多,很多場(chǎng)面我會(huì)永遠(yuǎn)記得,以這個(gè)故事向純粹的你們致敬。
后記
司馬:左老師,除了我給你們的筆記上提到的那些東西,你還準(zhǔn)備帶些什么寶貝一起穿越啊?
左村長(zhǎng):我要帶上我最喜歡的憤怒的小鳥(niǎo)周邊,哈哈哈!
總結(jié)
以上是生活随笔為你收集整理的用一张白纸推导出 RAFT 算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 画像的基础、原理、方法论(模型)和应用
- 下一篇: 广告中oCPX到底是如何进行优化的