3. 一步一步帶你實現(xiàn)raft(2B)

上來先看了2b的要求文檔。主要就是要handle log了。
里面強烈推薦了3篇文章绞旅,我認真做了閱讀。發(fā)現(xiàn)了我之前實現(xiàn)的一些問題温艇。
其中一個是來自中文的翻譯
比如:如果在超過選取領導人時間之前沒有收到來自當前領導人的AppendEntries RPC或者沒有收到候選人的投票請求因悲,則自己轉(zhuǎn)換狀態(tài)為候選人
原文:If election timeout elapses without receiving AppendEntries RPC from current leader or granting vote to candidate: convert to candidate.


image.png

區(qū)別就是一個是授予了投票再喚醒,另一個是只要收到rpc就喚醒勺爱。

文章鏈接來自hint:

  • Give yourself time to rewrite your implementation in light of lessons learned about structuring concurrent code. In later labs you'll thank yourself for having Raft code that's as clear and clean as possible. For ideas, you can re-visit our structure,locking and guide, pages.

實現(xiàn)的時候晃琳,發(fā)現(xiàn)除了是授予了投票,要喚醒。還有當選了LEADER也要喚醒卫旱。


image.png

重跑了下測試人灼,沒啥問題


image.png

第一步 閱讀論文5.3- 6之前

https://www.infoq.cn/article/raft-paper

第二步 FROM HINT

You will need to implement the election restriction (section 5.4.1 in the paper).

Raft 使用投票的方式來阻止沒有包含全部日志條目的服務器贏得選舉。一個候選人為了贏得選舉必須要和集群中的大多數(shù)進行通信顾翼,這就意味著每一條已經(jīng)提交的日志條目最少在其中一臺服務器上出現(xiàn)霞幅。如果候選人的日志至少和大多數(shù)服務器上的日志一樣新(up-to-date呻疹,這個概念會在下邊有詳細介紹)戳稽,那么它一定包含有全部的已經(jīng)提交的日志條目察署。RequestVote RPC 實現(xiàn)了這個限制:這個 RPC(遠程過程調(diào)用)包括候選人的日志信息,如果它自己的日志比候選人的日志要新拜姿,那么它會拒絕候選人的投票請求耗绿。

Raft 通過比較日志中最后一個條目的索引和任期號來決定兩個日志哪一個更新。如果兩個日志的任期號不同砾隅,任期號大的更新误阻;如果任期號相同,更長的日志更新晴埂。

這一步 應該上一章已經(jīng)實現(xiàn)了究反。


image.png

剩下一些HINT,在上一章基本都COVER住了儒洛,不用太多改動
比如

One way to fail the early Lab 2B tests is to hold un-needed elections, that is, an election even though the current leader is alive and can talk to all peers. This can prevent agreement in situations where the tester believes agreement is possible. Bugs in election timer management, or not sending out heartbeats immediately after winning an election, can cause un-needed elections.

image.png

第三步 實現(xiàn)START

根據(jù)方法描述精耐,實現(xiàn)如下


image.png
image.png

第四步 實現(xiàn)startAppendLog

搜索到//now only for heartbeat
然后開始把這個方法從只是發(fā)送HEART BEAT 到真的開始發(fā)日志。

論文重點

當這個條目被安全的復制之后(下面的部分會詳細闡述)琅锻,領導人會將這個條目應用到它的狀態(tài)機中并且會向客戶端返回執(zhí)行結(jié)果卦停。如果追隨者崩潰了或者運行緩慢或者是網(wǎng)絡丟包了,領導人會無限的重試 AppendEntries RPC(甚至在它向客戶端響應之后)直到所有的追隨者最終存儲了所有的日志條目恼蓬。

領導人跟蹤記錄它所知道的被提交條目的最大索引值惊完,并且這個索引值會包含在之后的 AppendEntries RPC 中(包括心跳 heartbeat 中),為的是讓其他服務器都知道這條條目已經(jīng)提交处硬。一旦一個追隨者知道了一個日志條目已經(jīng)被提交小槐,它會將該條目應用至本地的狀態(tài)機(按照日志順序)

  • 如果在不同日志中的兩個條目有著相同的索引和任期號,則它們所存儲的命令是相同的荷辕。
  • 如果在不同日志中的兩個條目有著相同的索引和任期號凿跳,則它們之間的所有條目都是完全一樣的。

第一條特性源于領導人在一個任期里在給定的一個日志索引位置最多創(chuàng)建一條日志條目疮方,同時該條目在日志中的位置也從來不會改變控嗜。第二條特性源于 AppendEntries 的一個簡單的一致性檢查。當發(fā)送一個 AppendEntries RPC 時骡显,領導人會把新日志條目緊接著之前的條目的索引位置和任期號都包含在里面疆栏。如果追隨者沒有在它的日志中找到相同索引和任期號的日志曾掂,它就會拒絕新的日志條目。這個一致性檢查就像一個歸納步驟:一開始空的日志的狀態(tài)一定是滿足日志匹配原則的承边,一致性檢查保證了當日志添加時的日志匹配原則遭殉。因此石挂,只要 AppendEntries 返回成功的時候博助,領導人就知道追隨者們的日志和它的是一致的了。

下面是最關鍵的一段話

為了使得追隨者的日志同自己的一致痹愚,領導人需要找到追隨者同它的日志一致的地方富岳,然后刪除追隨者在該位置之后的條目,然后將自己在該位置之后的條目發(fā)送給追隨者拯腮。這些操作都在 AppendEntries RPC 進行一致性檢查時完成窖式。領導人給每一個追隨者維護了一個nextIndex,它表示領導人將要發(fā)送給該追隨者的下一條日志條目的索引动壤。當一個領導人開始掌權時萝喘,它會將nextIndex初始化為它的最新的日志條目索引數(shù) +1(圖 -7 中的 11)。如果一個追隨者的日志和領導者的不一致琼懊,AppendEntries 一致性檢查會在下一次 AppendEntries RPC 時返回失敗阁簸。在失敗之后,領導人會將nextIndex遞減然后重試 AppendEntries RPC哼丈。最終nextIndex會達到一個領導人和追隨者日志一致的地方启妹。這時,AppendEntries 會返回成功醉旦,追隨者中沖突的日志條目都被移除了饶米,并且添加所缺少的上了領導人的日志條目。一旦 AppendEntries 返回成功车胡,追隨者和領導人的日志就一致了檬输,這樣的狀態(tài)會保持到該任期結(jié)束。

需要知道GO 切片知識
https://stackoverflow.com/questions/27055626/concisely-deep-copy-a-slice?rq=1

image.png

因為 //在失敗之后匈棘,領導人會將nextIndex遞減然后重試 AppendEntries RPC
所以要包在一個FOR循環(huán)里褪猛,失敗就重試,成功就RETURN,因為變成了循環(huán) 注意DEFER unlock就不能用在循環(huán)體里了羹饰,不然就死鎖了伊滋。

image.png

image.png

下圖我們發(fā)現(xiàn)第一條我們實現(xiàn)了,后面都沒有完整實現(xiàn)队秩。第二條的前半段在之前Start()里實現(xiàn)的笑旺。后半段把條目應用到狀態(tài)機后響應客戶端還沒實現(xiàn)。

image.png

領導人規(guī)則的第五條馍资,因為是要看MATCH INDEX筒主,而MATCH INDEX只在發(fā)送成功的時候會更新。所以為了減少不必要的CHECK,我們可以放在reply.Success里來做
注意 這里中文是病句
因為原文為

If there exists an N such that N > commitIndex, a majority of matchIndex[i] ≥ N, and log[N].term == currentTerm: set commitIndex = N (§5.3, §5.4).

我們一點一點來乌妙,先處理reply.Success

image.png

第五步 實現(xiàn)updateCommitIndex

就是根據(jù)上面的英文來實現(xiàn)使兔,超過半數(shù)的話,就排序然后找中間偏大的那個藤韵,來比即可虐沥。

注意在找超過半數(shù)的時候,很容易忘記LEADER自己的INDEX沒有更新泽艘,造成臨界點剛好沒到半數(shù)欲险。


image.png

到此為止,我們基本實現(xiàn)了startAppendLog匹涮,第一個//now only for heartbeat變?yōu)榱苏娴臅嗀PPEND LOG的方法

下面開始寫HANDLER

第六步 實現(xiàn)AppendEntries

這里論文也有寫怎么做天试,介于中文翻譯的質(zhì)量確實不理想。這里貼個英文圖


image.png

因為這邊開始會RETURN了然低。所以我們要把收到APPEND ENTRIES 這個RPC的CHANNEL 喚醒的操作放到DEFER里去


image.png

在UTIL.GO 里定義MIN INT 方法


image.png

不知道你有沒有注意到我們一直在更新commitIndex喜每,但是我們沒有更新lastApplied
這里還有一條所有服務器的規(guī)則

如果commitIndex > lastApplied,lastApplied自增雳攘,將log[lastApplied]應用到狀態(tài)機(5.3 節(jié))

第七步 思考何時updateLastApplied

為了減少調(diào)用次數(shù)带兜,我們只要思考何時會更新COMMITINDEX
發(fā)現(xiàn)2處


image.png

image.png

第八步 實現(xiàn)updateLastApplied

image.png

調(diào)試

BUG 1

按照老師的建議,我們先測2B最簡單的来农,發(fā)現(xiàn)出問題了鞋真,INDEX越界
排查了下。
這邊應該是=沃于,我之前寫成+=


image.png

BUG2

數(shù)組越界的錯沒了涩咖。下面的錯非常棘手。
他報了這個 config.go:465: one(100) failed to reach agreement
在仔細閱讀了https://thesquareplanet.com/blog/students-guide-to-raft/
我依然沒有任何收獲繁莹。
決定在關鍵位置打LOG檩互。大概率是因為APPEND ENTRIES 這個RPC 有邏輯沒做對。
所以我在發(fā)送RPC前后咨演,加了LOG

image.png

得到信息如下


image.png

感覺沒太大問題闸昨,不過可以知道REPLY 是SUCCESS的。說明HANDLER里面邏輯是正確的薄风。
因為最終服務器需要在APPLY CHANNEL饵较,讀信息
所以我們在CHANNEL出去的時候做一個打印,看下有沒有出去遭赂。


image.png

隨后因為走的分支是REPLY.SUCCESS
在這個里面把對應的參數(shù)也打印一下循诉。


image.png

打印結(jié)果,發(fā)現(xiàn)消息完全沒有出去的跡象撇他。


image.png

分析LOG可以得知
matchIndex 一開始是-1茄猫,后來變成了0
而我們初始化的時候就對matchIndex 初始化為了0.
再增加了一個元素后狈蚤,依然是0,而updateCommitIndex的關鍵就是N > commitIndex, a majority of matchIndex[i] ≥ N划纽。

那么問題就是即使所有NODE 都收到了那個元素脆侮,然而matchIndex最大也是到0, 0是不會超過commitIndex 初始化的0
再回過去看commitIndex 和 matchIndex的定義


image.png

按照這個定義的理解勇劣,只有被提交的最高索引靖避。那么初始化怎么說也應該是-1呀。

我開始按照我的思路改成-1芭毙,APPLYMSG出去了筋蓖⌒对牛可是測試需要的INDEX是我返回的INDEX還要+1.我又不好改測試退敦。

一陣無語的時候我發(fā)現(xiàn)


image.png

這么重要的信息中文翻譯里竟然沒有

改動如下0改稱1,加上注釋


image.png

PASS 2B

image.png

Test Race

image.png

TEST.SH

因為有些并發(fā)的問題蚣抗,可能不是每次重現(xiàn)侈百,所以要盡可能多測來確定有沒有問題。
我把這個標準定為1000次翰铡, 可是1000次要跑很久钝域。所以我通過SHELL來處理這個問題。
SHELL 腳本如下锭魔,每處理完10次TEST打一個進度

#!/bin/bash
export PATH="$PATH:/usr/lib/go-1.9/bin"

rm res -rf
mkdir res


test_list=(
2A
2B)
for i in `seq 1000`
do
 if [ $(($i % 10)) -eq 0 ]
 then
     echo $i
 fi
 for j in ${test_list[@]}
 do
     go test -run $j &> ./res/$j
     if [ $? -ne 0 ]
     then
         echo "run failed."
         exit 1
     fi
 done
done
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末例证,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子迷捧,更是在濱河造成了極大的恐慌织咧,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漠秋,死亡現(xiàn)場離奇詭異笙蒙,居然都是意外死亡,警方通過查閱死者的電腦和手機庆锦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門捅位,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搂抒,你說我怎么就攤上這事艇搀。” “怎么了求晶?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵焰雕,是天一觀的道長。 經(jīng)常有香客問我誉帅,道長淀散,這世上最難降的妖魔是什么右莱? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮档插,結(jié)果婚禮上慢蜓,老公的妹妹穿的比我還像新娘。我一直安慰自己郭膛,他們只是感情好晨抡,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著则剃,像睡著了一般耘柱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上棍现,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天调煎,我揣著相機與錄音,去河邊找鬼己肮。 笑死士袄,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的谎僻。 我是一名探鬼主播娄柳,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼艘绍!你這毒婦竟也來了赤拒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤诱鞠,失蹤者是張志新(化名)和其女友劉穎挎挖,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體般甲,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡肋乍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了敷存。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片墓造。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锚烦,靈堂內(nèi)的尸體忽然破棺而出觅闽,到底是詐尸還是另有隱情,我是刑警寧澤涮俄,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布蛉拙,位于F島的核電站,受9級特大地震影響彻亲,放射性物質(zhì)發(fā)生泄漏孕锄。R本人自食惡果不足惜吮廉,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望畸肆。 院中可真熱鬧宦芦,春花似錦、人聲如沸轴脐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽大咱。三九已至恬涧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碴巾,已是汗流浹背溯捆。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留餐抢,地道東北人现使。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓低匙,卻偏偏與公主長得像旷痕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子顽冶,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

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

  • 尋找一種易于理解的一致性算法(擴展版) 摘要 Raft 是一種為了管理復制日志的一致性算法欺抗。它提供了和 Paxos...
    枝葉君閱讀 2,647評論 0 15
  • 尋找一種易于理解的一致性算法(擴展版) 摘要 Raft 是一種為了管理復制日志的一致性算法。它提供了和 Paxos...
    yflau閱讀 986評論 0 1
  • Raft是一種為了管理日志復制的一致性算法强重。它提供了和Paxos算法相同的功能和性能绞呈,但是它的算法結(jié)構和Paxos...
    WithLin閱讀 807評論 0 1
  • 復制狀態(tài)機 共識算法是從復制狀態(tài)機的背景下提出的。在這種方法中间景,一組服務器上的狀態(tài)機產(chǎn)生相同狀態(tài)的副本佃声,并且在一些...
    小睿千萬別禿頭閱讀 915評論 0 0
  • 作者:云海峰 如果你決定要走 我會笑著放手 青春時代的遺憾 我已大聲說出口 沒有什么留戀 更不會有什么憂愁 癡情幾...
    敕勒川云海峰閱讀 182評論 1 2