可能是你能找到的最完整最詳細的中文版的Raft算法說明博客
內容來源都是原生的論文,可以保證內容的可靠性,并且對論文里面的很多細節(jié)做了擴展說明
章節(jié)介紹
Raft系列1 基本概念(原創(chuàng))
http://www.itdecent.cn/p/0e695b4be92f
Raft系列2 核心概念(角色&Rules)
http://www.itdecent.cn/p/00e89b5e9669
Raft系列3 總體心得
http://www.itdecent.cn/p/496981a62bbe
Raft系列4 任期和選舉
http://www.itdecent.cn/p/aa137c94ac0b
Raft系列5 客戶端設計
http://www.itdecent.cn/p/bb7388b0cde6
Raft系列6 AppendEntries RPC
http://www.itdecent.cn/p/d3247e2203a6
Raft系列7 快照技術和動態(tài)修改配置
http://www.itdecent.cn/p/12e8bc4f384c
Raft系列8 一致性分析
http://www.itdecent.cn/p/6344f202b7e5
Raft集群的模型介紹

圖形來看有2個內容:request的完整流程走向、一個Node的主要構成
- 數據流程的走向:
- 客戶端發(fā)起一個命令,比如設置 x->3,過程1
- 會通過Raft集群的一致性模型的管理,最終request會傳達到leader節(jié)點
- leader會把這個操作步驟寫在自己本地日志,并且會通過一致性模型,通知到其它的各個follower節(jié)點,過程2
- 超過半數follower復制反饋成功,leader再把這個操作應用到狀態(tài)機中,可能以前狀態(tài)機中沒有x的值,或者x的值為2,最終都會在狀態(tài)機中生成,x->3,過程3
- 最后再返回給客戶端,操作成功,過程4
- 一個Node的主要構成: 一致性模型、日志、狀態(tài)機
log[](command)

log除了有index的概念(下標從1開始),還有term的概念(這個log是在哪個term添加的),另外log本身就是一個命令 command,比如 x->3
raft集群中的log指的是一個命令,或者說是一個操作而不是一個value,比如 x 設置為 3,或者 y 設置為 5.
log[]中有的命令數組,不一定是最終會執(zhí)行的命令。只有半數通過,最終leader commit了,才會apply到狀態(tài)機中。
一個log[]完成不了一致性的功能,還依賴Node的commitIndex(半數通過了的命令),lastApplied(最終運用到狀態(tài)機當中的命令)下標,最終實現一致性
關于log的操作有3種:copy、commit、apply。 copy是leader把新日志發(fā)送給所有follower的過程,commit是leader統計到超過半數的節(jié)點完成了復制,然后更新自己的commitIndex的過程,apply是完成更新commitIndex時,節(jié)點把最新commit的命令,按照順序執(zhí)行到自己的狀態(tài)機里面的一個過程
狀態(tài)機state Machine
- 每個節(jié)點都有一個狀態(tài)機,一個Raft集群就是一組狀態(tài)機,zookeeper也是通過這種方式實現一致性管理的(復制狀態(tài)機)
- 注意狀態(tài)機和日志的區(qū)別,日志存儲的是數據操作的操作日志,狀態(tài)機存儲的是數據操作的結果值。
比如客戶端發(fā)起3個操作,x:1, x:2, x:3,則有3個操作日志,但是存儲在狀態(tài)機中只有1個鍵值對: x:3。
狀態(tài)機的作用:
- 存儲的數據是這個值的最終態(tài),而不是過程態(tài)(比如 x:3),可以提升查詢的效率,直接返回結果
- 存儲半數節(jié)點都通過的數據(x:3是半數節(jié)點一致通過的結果,而log里面的命令,不一定會被執(zhí)行,可能因為leader的失敗,而導致這個命令刪除或者被覆蓋掉)
日志的作用:管理和同步數據用的。日志里面的數據(command)可能會被覆蓋。
Server的詳細組成

每個狀態(tài)機因為role不同,會存儲不同的內容
會持久化的內容: (每次響應RPC的時候都會update值到硬盤上)
- currentTerm
- votedFor
- log[]: log有index,term,command等概念
不會持久化的內容:
每個節(jié)點都有一個狀態(tài)機,來存儲所有command按照日志順序結構執(zhí)行的最終結果
所有臨時存儲的內容:commitIndex、lastApplied(節(jié)點收到leader的最新的commitIndex,更新完自己的commitIndex之后,開始把最新的log里面的command apply到狀態(tài)機中,操作結束后更新lastApplied的值),所以理論上commitIndex的值和lastApplied的值大部分的時候是相等的,或者講當server發(fā)現lastApplied<commitIndex,會執(zhí)行apply的操作
Leader節(jié)點存儲的臨時的內容(每次重新選舉會初始化):
- nextIndex[] :下一個待發(fā)送日志的下標,初始化值是 新leader的last Log Index +1
- matchIndex[]:日志最匹配的一個下標,初始化值是0
非常巧妙的一個設計,理論上正常情況下,matchIndex=nextIndex-1
比如leader的日志條目數是10個且最新沒有要添加的日志,其中4個follower,數據都是同步的,那么nextIndex[] : 11,11,11,11 。matchIndex[] 為 10,10,10,10
如果當前l(fā)eader有一個新的日志要添加,那么AppendEntries RPC到各個follower,response成功后,更新nextIndex[] 和matchIndex[]
主要的發(fā)揮用途是在狀態(tài)異常的時候
- 老leader掛了,新leader當選,默認nextIndex為11,matchIndex為0,發(fā)送一次同步之后,如果數據都一樣,那么更新為 nextIndex為11,matchIndex為10。
如果數據有gap,AppendEntries RPC response false,那么nextIndex會遞減1,leader發(fā)送的log為 log[10],如果還失敗 繼續(xù)遞減,直到返回true,再挨個的同步數據
- 一個計時器(計算心跳超時和 選舉超時)
- 一個統計器(leader會統計成功復制的節(jié)點個數是否超過半數,candidate會統計獲得的投票數是否超過半數)
注意事項:
- leader為了提升性能,會并發(fā)性的給所有follower發(fā)消息,但是每個RPC的內容可能是不同的,是根據nextIndex[]來發(fā)的,如果所有的follower的狀態(tài)都一致,那么nextIndex[]的值都一樣
- 感覺matchIndex[]和nextIndex[]有冗余,因為matchIndex[]的index+1就是nextIndex的值
RPC
- Raft中的RPC分為2種大的類型:選舉消息和狀態(tài)同步類消息。按照數據結構來分為3種RPC:RequestVote RPC、AppendEntries RPC、InstallSnapshot RPC。按照功能分為5種RPC(選舉、同步log、心跳、新leader的宣誓主權的消息、同步快照)。
- 日志同步類消息分為:AppendEntries RPC和 InstallSnapshot RPC(快照方式同步數據)
- AppendEntries RPC按照功能來分:heartbeat功能的消息、同步log的消息、宣誓主權的同步內容為空的log消息(可以確保老leader遺留給新leader的消息仍然會被執(zhí)行)
- Raft集群間的RPC消息是雙向的,就是消息發(fā)起人發(fā)起一個消息,消息接收人會返回一個結果,這才構成一個完整的消息
- 如果 follower 崩潰或者運行緩慢,或者網絡丟包,leader 會不斷地重試 AppendEntries RPC(即使已經回復了客戶端)直到所有的 follower 最終都存儲了所有的日志條目
- Raft 的 RPCs不僅交互失敗會發(fā)起重試,而且這種重試都是冪等的,所以這樣的重試不會造成任何傷害。例如,一個 follower 如果收到 AppendEntries 請求但是它的日志中已經包含了這些日志條目,它就會直接忽略這個新的請求中的這些日志條目。
參考
主要參考Stanford University的原生論文以及中文的翻譯版本
Raft算法中文版(論文翻譯)
https://www.cnblogs.com/linbingdong/p/6442673.html