參考視頻:
https://www.youtube.com/watch?v=vYp4LYbnnW8&feature=youtu.be
步驟分解
1.Leader選舉
選一個server當leader
檢測到crashes,就選個新的leader
2.複製Log(正常情況)
Leader接受client請求,本地append log
leader將log複製到其他server(覆寫不一致的地方)
3.安全
保持logs是一致的
只有獲得最新的logs的servers可以成爲leader
Server狀態以及RPCs
初始化是Follower
(被動的,僅僅傻傻地等待心跳)
如果一直等不到,那就成爲Candidate
。(發起RequestVote
RPCs,大家來投我當leader啊)
- 如果獲得大多數的支持,贏得了選舉,就成爲了leader(發起
AppendEntries
RPCs: 複製我的log,發心跳來維持我的統治)
只要發現更高的term
,我就乖乖變回follower。
Terms(決定哪些信息是過期的)
- 每個term最多1個leader
- 有些term是沒有leader的(選舉失敗)
- 每個server維護一個
current term
的值(但是都不知道全局的term是啥樣,沒有上帝視角)
– 每次RPC都交換信息
– 發現我的小夥伴有更新的term?好,那你是大哥,我信你,我把本地的term改成跟你一樣,我還是乖乖當follower
– 來一個RPC請求有過期的term?告訴他你可能搞錯了兄弟。reply error
Leader選舉
成爲Candidate
本地currentTerm++,自己給自己投一票
發送RequestVote RPCs給其他servers(超時重試上一步)
獲得大多數支持:成爲leader,發心跳。
收到leader的RPC,乖乖當follower。
選舉的正確性
Safety:每個term最多一個winner
每個server 每個term只投一票(持久化到硬盤)
贏得大多數選票才能贏得選舉
Liveness:總有人最終要贏
選舉超時: 是個在[T,2T]的隨機數,如150-300ms
一般情況,有一個server會首先超時然後贏得選舉
當T >> 廣播時間的時候,工作良好
隨機數是你的好朋友,實踐證明比ranking好使
具體是ranking可能要考慮很多corner case
隨機數的邏輯相對簡單清晰
常規操作
客戶端發送command到leader
leader 本地append log
leader 發送 AppendEntries
RPCs給所有Followers
一旦有新的entry committed
- Leader 在自己的狀態機執行 command,返回結果給client
- Leader在接下來的
AppendEntries
中通知Followers,哪些entries被committed - Followers在自己的狀態機執行 command
如果followers 掛掉了或者反應遲鈍
- Leader會重試
AppendEntries
直到成功
常規情況下性能不錯
- 一個成功的RPC發給大多數的servers
Log 結構
每個log 都保存了term
什麼叫committed?
大多數的機器都保存了一樣的log。如同上圖的committed entries
Log 不一致
一旦有機器掛了,就會出現不一致
遇到這種奇怪的情況,Raft怎麼解決呢?
目標:最小化special code來修復不一致
- Leader就認爲自己的log是對的
- 正常的operation可以修復所有不一致
Log Matching Property
如果不同的servers 的log entries有一樣的index 和 term
- 他們保存了相同的command
- 所有前面的entries都相同
如果一個entry committed,所有前面的entries都被committed
AppendEntries 一致性檢查
AppendEntries
RPCs 包括了 前一個
entry的<index, term>
Follower 必須包含matching entry,否則他會拒絕請求
- Leader就需要用更低的log索引請求
實現一個induction step
,保證Log Matching Property
看圖好理解:
注意前提:leader的log永遠正確
問題來了,我們怎麼知道leader的log就一定正確呢?
Safety: Leader Completeness
一旦一個log entry committed,所有未來的leader都要保存這個entry。
servers如果沒有完整的log,是不能被選舉的
- Candidates 在RequestVote RPCs中包括了index和term
- 每個server在投票的時候,如果自己的log更好,那就拒絕,投反對票
- 怎麼作爲好? 按照<lastTerm,lastIndex> 排序
上面這個圖這個例子,如何比較呢
先看誰的lastTerm大,首先S5就幹不過別的。然後再看誰更長(lastIndex大)S3比S1和S4強。所以S3是真正的大哥。
有個問題:按照前面的邏輯,如果S1先timeout,先發的RequestVote,S3後發,S2,S4,S5都幹不過S1,都會同意讓S1當老大,那S3不就當不了老大了?
彩蛋:Raft這個名字怎麼取的
Replicated And Fault Tolerant的縮寫
複製的,容錯的
Raft代表木筏:可以幫助你躲過Paxos這個萬惡的大山😂