raft 系列解讀(4) 之 etcd-raft學(xué)習(xí)

好的實現(xiàn)埋合,看看別人怎么寫的卧须,github

大多數(shù)Raft的實現(xiàn)都是整體設(shè)計,包括存儲處理撰糠,消息序列化和網(wǎng)絡(luò)傳輸酥馍,但是本raft庫在實現(xiàn)的時候只實現(xiàn)了最核心的算法,換來了靈活性和性能阅酪,網(wǎng)絡(luò)和disk IO部分都由使用者實現(xiàn)旨袒,使用者需要實現(xiàn)自己的消息傳送層,同時术辐,需要自己實現(xiàn)持久化部分來存儲Raft log和state砚尽。
為了實現(xiàn)Raft庫的可測性,庫在實現(xiàn)的時候?qū)aft建模為一個狀態(tài)機辉词,輸入是消息必孤,可能是本地時間的更新或者網(wǎng)絡(luò)消息,狀態(tài)機的輸出是一個3元組:{[]Messages, []LogEntries, NextState}瑞躺。

第一步是使用敷搪,怎么使用raft來搭建自己的key-value系統(tǒng)

etcd-raft代碼走讀

node-run

上面是raft中一個node做的事,Node代表raft集群中的一個節(jié)點幢哨,剛開始node是follower赡勘,然后隨著tickc的進行,開始進入選舉捞镰,raft在變?yōu)閒ollower的時候做了下面幾件事:
becomeFollower

初始化了tick函數(shù)tickElection闸与,用來開始選舉,做判斷后岸售,調(diào)用Step
選舉函數(shù)

判斷消息類型為MsgHup践樱,于是進入campaign
進行選舉

選舉函數(shù)中做的事情
選舉

轉(zhuǎn)換成candidate時,開始一個選舉:

  1. 遞增currentTerm;投票給自己;
  2. 重置election timer;
  3. 向所有的服務(wù)器發(fā)送 RequestVote RPC請求

成為candidate

接著看下send函數(shù)
raft.send

send函數(shù)中將消息存儲了msgs中,在哪兒消費呢凸丸?通過讀取newReady來返回Ready
圖片

此時又返回到node.run中拷邢,此時因為會進入case readyc <- rd分支
圖片

在里面做的事情
圖片

msgs因為已經(jīng)讀取過了,設(shè)置為空屎慢,并且會賦值advancec解孙,進行到這readyc中已經(jīng)有一個數(shù)據(jù)坑填, 而此channel會在函數(shù)Ready中返回給外面,下面接著看誰會去讀Ready

func (n *node) Ready() <-chan Ready { return n.readyc }

讀取Ready的是應(yīng)用程序弛姜,看下Ready()函數(shù)的說明

//=> 讀取到當(dāng)前狀態(tài)脐瑰,當(dāng)從Ready()取出狀態(tài)后,需要調(diào)用 Advance
//=> 注意:只有當(dāng)所有提交的entries都應(yīng)用后廷臼,才會調(diào)用下一個 Ready 的狀態(tài)

我們回到之前的選舉上苍在,讀取到的Ready里面包含了Vote消息,我們會調(diào)用網(wǎng)絡(luò)層發(fā)送消息出去荠商,并且調(diào)用Advance()寂恬,而此時其他Node接收到網(wǎng)絡(luò)層消息后,會調(diào)用Step()函數(shù)莱没,在成為candidate的時候初肉,我們設(shè)置了step函數(shù)為stepCandidate

stepCandidate

自后調(diào)用了node的send函數(shù)饰躲,此時是拒絕的牙咏,因為已經(jīng)是candidate狀態(tài)了,而如果是follower嘹裂,其處理函數(shù)是stepFollower,
stepFollower

其規(guī)則就是之前說到的:

如果本地的voteFor為空或者為candidateId,并且候選者的日志至少與接受者的日志一樣新,則投給其選票

進行到這妄壶,我們看到了follower在收到vote rpc后的處理,下面是candidate的處理了寄狼。

回到之前調(diào)用Ready()丁寄,接著應(yīng)該調(diào)用Advance,

Advance

里面會設(shè)置advancec,好了泊愧,運行到這伊磺,我們又要回到node.run中了

此時的狀態(tài)是:candidate,advancec中有數(shù)據(jù)删咱,接著來看candidate在發(fā)送出vote rpc奢浑,接收到響應(yīng)的處理,網(wǎng)絡(luò)層的Send函數(shù)是:

Transport.Send

Send會調(diào)用Peer.send腋腮,函數(shù)注釋說:此函數(shù)是非阻塞的,不保證請求一定能被peer收到
Peer.send

一般常理我們發(fā)送后壤蚜,等待響應(yīng)后再處理即寡,但是找了很久也沒找到常理函數(shù),這個時候袜刷,我們再去看下follower對于投票的處理
send.MsgVoteResp

發(fā)現(xiàn)在響應(yīng)上也是通過發(fā)送一個消息來響應(yīng)的聪富,因此我們此時可以看到peer之間的交互不是傳統(tǒng)意義上的request-response模型了。

我們?nèi)タ磳τ?code>MsgVoteResp的處理著蟹,其入口都是通過調(diào)用node.Step函數(shù)墩蔓,此時如果得到大多數(shù)票選梢莽,則成為leader

MsgVoteResp處理

看becomeLeader函數(shù)
becomeLeader

在leader函數(shù)中,最重要的就是發(fā)送命令了奸披,我們看看這個過程

這是通過node.Propose函數(shù)實現(xiàn)的

node.Propose

到最后又是通過step函數(shù)
stepLeader

里面挨個調(diào)用send函數(shù)

func (r *raft) bcastAppend() {
    for id := range r.prs {
        if id == r.id {
            continue
        }
        r.sendAppend(id)
    }
}

sendAppend

看完發(fā)送端昏名,接著看follower的接收端處理
handleAppendEntries

細看handleAppendEntries函數(shù),就是去做raft協(xié)議中規(guī)定的操作了
圖片

maybeAppend中阵面,會去嘗試更新committed index轻局,然后接著看AppendResp的處理
AppendResp

去檢查各個peer的matchIndex,然后嘗試更新commitIndex

下一個問題样刷,接著去看commitIndex > lastAppied后仑扑,在哪兒去應(yīng)用log到狀態(tài)機的
這是通過node.runreadycadvancec來實現(xiàn)的

node.run

上面就是etcd中raft的大致流程,有一個機遇raft實現(xiàn)的簡單key-value系統(tǒng)置鼻,github地址:https://github.com/zhuanxuhit/distributed-system/tree/master/etcd-raft

讀完代碼后镇饮,最大的一個感受是整個node在實現(xiàn)的時候都是無鎖的,其技巧是通過go的channel將所有請求串行化箕母,然后另一個特點是根據(jù)不同的狀態(tài)储藐,設(shè)置不同的處理函數(shù),整個實現(xiàn)非常的清晰司蔬,因為每個狀態(tài)針對每個請求的處理都是非常明確的邑茄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市俊啼,隨后出現(xiàn)的幾起案子肺缕,更是在濱河造成了極大的恐慌,老刑警劉巖授帕,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件同木,死亡現(xiàn)場離奇詭異,居然都是意外死亡跛十,警方通過查閱死者的電腦和手機彤路,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芥映,“玉大人洲尊,你說我怎么就攤上這事∧纹” “怎么了坞嘀?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惊来。 經(jīng)常有香客問我丽涩,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任矢渊,我火速辦了婚禮继准,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘矮男。我一直安慰自己移必,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布昂灵。 她就那樣靜靜地躺著避凝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪眨补。 梳的紋絲不亂的頭發(fā)上管削,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音撑螺,去河邊找鬼含思。 笑死,一個胖子當(dāng)著我的面吹牛甘晤,可吹牛的內(nèi)容都是我干的含潘。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼线婚,長吁一口氣:“原來是場噩夢啊……” “哼遏弱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起塞弊,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤漱逸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后游沿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饰抒,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年诀黍,在試婚紗的時候發(fā)現(xiàn)自己被綠了袋坑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡眯勾,死狀恐怖枣宫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吃环,我是刑警寧澤也颤,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站模叙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鞋屈。R本人自食惡果不足惜范咨,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一故觅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渠啊,春花似錦输吏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至躲查,卻和暖如春它浅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背镣煮。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工姐霍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人典唇。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓镊折,卻偏偏與公主長得像,于是被迫代替她去往敵國和親介衔。 傳聞我的和親對象是個殘疾皇子恨胚,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351

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