實驗說明:https://pdos.csail.mit.edu/6.824/labs/lab-raft.html
可視化:http://thesecretlivesofdata.com/raft/
實現目標
根據論文Figure 2和5.2實現Raft系統的選主和心跳
編程環境
1. 最終我們希望的目標是可以對raft目錄下test文件進行測試,2A實現主要進行的測試代碼有前2個,執行"go test -run 2A
"
2. 在進行編程和調試時,我主要使用了goland編輯器,需要先設置下gopath,如下
3. 設置完成後,如果你直接執行下面的函數的話,會報錯,是因爲項目使用的import方式的問題
這個問題的原因在於在raft/raft.go、raft/config.go和raft/labrpc.go的代碼裏面有一個Import,是直接import了"../labrpc"和"../labgob",我們將GOPATH修改之後,需要把它們改成"import labrpc"和"import labgob"
當然也可以在不修改GOPATH的情況下進行修復這個問題,可以放置
6.824到原本src/github或者哪個項目裏面,如下
完成上述步驟之後就可以直接運行test代碼了
整體流程
1. 整個選主的流程其實就是一個raft server不斷監控自身狀態做出選擇的過程,整個過程大致如下:
- 初始化一個raft server,配置對raft的參數(參考論文figure 2裏面的State表格),相較與論文,我們這裏還需要多一個state,表示raft server的身份;還有一個expireTime,用於表示過期時間(follower和candidate的進行投票請求的時間,也是leader進行發送心跳請求的時間),這個過期時間需要我們不斷進行動態更新
- 如果自身狀態是Follower的話,發生過期時間已經到達,就轉換爲Candidate,然後發送投票請求給其他節點
- 如果自身狀態是Leader的話,發生過期時間已經到達,就給其他節點發送心跳請求
- candidate在發送投票請求時
- term先自增1,刷新自己的過期時間
- 給所有的peers發送投票請求,並統計獲得的投票數,只要投票數大於半數,就可以成爲Leader,成爲leader之後,需要直接發送心跳請求(統計需要使用原子計數)
- 期間如果收到了一個term比自身要大的請求,便將自身狀態設置爲Follower,標記本次選主失敗,已經有更新的leader存在
- leader發送心跳請求時
- 先刷新自身的過期時間,準備下次心跳
- 給所有peers發送心跳請求,如果收到一個term比自身大的響應,就設置自身的term,再回退到follower狀態,刷新自身的過期時間
- 在節點收到投票請求時,處理邏輯爲
- 比較自身的term與請求攜帶的term(對應candidate的term),如果自身的term比請求推薦的term大的話,將響應攜帶的term設置爲自身的term,響應不投票,並結束
- 如果自身的term比請求攜帶的term小,就把自身的term設置爲請求攜帶的term,狀態設置爲Follower,並比較兩方的日誌狀態(當前沒有進行log操作,都是一樣新的),設置自身的投票對象id,再刷新自己的過期時間
- 在節點收到心跳請求時(log爲空的log請求)
- 先判斷term大小,如果自身term比較大的話,設置響應的term爲自身term,返回false
- 否則將自身狀態更新爲Follower,投票對象設置爲-1,自身term設置爲請求攜帶的term,並更新過期時間,返回成功