接上一個對於Raft的分析,這是文章的鏈接地址:https://blog.csdn.net/karry_zzj/article/details/102943617
我們這一文章主要分析一下我們的作業的代碼。
作業主要就是在一個框架下實現相關算法。
論文中提供了一張簡要的raft算法總結圖(論文figure 2),可以作爲我們代碼實現的指導。
從這張圖我們可以具體分析一下有如下幾個主要內容:
- State:即服務器的狀態
- RequestVote RPC:投票請求的RPC
- AppendEntries RPC:附加日誌的RPC
- Rules for Servers:服務器的規則
等會我們具體來分析。
項目結構
首先我們來看一下這個作業項目的結構:
其中我們主要用到如下文件:
- labrpc 文件夾下的
labrpc.go
,test_test.go
- raft 文件夾下的
config.go
、raft.go
我們首先打開test_test.go
文件,我們可以發現有如下函數,而此函數就是整個項目測試leader election的入口處。
func TestInitialElection(t *testing.T) {
servers := 3
cfg := make_config(t, servers, false)
defer cfg.cleanup()
fmt.Printf("Test: initial election ...\n")
// is a leader elected?
cfg.checkOneLeader()
// does the leader+term stay the same there is no failure?
term1 := cfg.checkTerms()
time.Sleep(2 * RaftElectionTimeout)
term2 := cfg.checkTerms()
if term1 != term2 {
fmt.Printf("warning: term changed even though there were no failures")
}
fmt.Printf(" ... Passed\n")
}
我們看到有這一行代碼:
cfg := make_config(t, servers, false)
當我們點進去make_config
這個函數,其實就進入到了 config.go
這個文件中。
其中有三行是創建 Rafts :
// create a full set of Rafts.
for i := 0; i < cfg.n; i++ {
cfg.logs[i] = map[int]int{}
cfg.start1(i)
}
我們再點進去 start1
這個函數,發現有這樣的一行:
rf := Make(ends, i, cfg.saved[i], applyCh)
而這個Make
函數就是在 raft.go
文件中寫的,所以我們這樣便知道了整個項目的一個運行流程:
而Make就是我們需要寫的一個函數,其中需要gorutine進行leader election。
Raft State
論文figure2 中有標註了State的參數:
那麼我們首先要在 Raft 結構體中加入如下參數,代碼如下:
//
// A Go object implementing a single Raft peer.
//
type Raft struct {
mu sync.Mutex
peers []*labrpc.ClientEnd
persister *Persister
me int // index into peers[]
// Your data here.
// Look at the paper's Figure 2 for a description of what
// state a Raft server must maintain.
state State // Raft的狀態
leaderid int
// Persistent state on all servers:
currentTerm int // 服務器最後知道的任期號
votedFor int // 在當前任期內收到選票的候選人 id
logs []LogEntry // 日誌條目: 每個條目包含狀態機的要執行命令和從領導人處收到時的任期號
// Volatile state on all servers:
commitIndex int // 已知的被提交的最大日誌條目的索引值
lastApplied int // 被狀態機執行的最大日誌條目的索引值
// Volatile state on leaders
nextIndex []int // 對於每一個服務器,記錄需要發給它的下一個日誌條目的索引
matchIndex []int // 對於每一個服務器,記錄已經複製到該服務器的日誌的最高索引值
// two timeout settings which control elections
electionTimeout int
heartbeatPeriod int
latestIssueTime int64 // 最新的leader發送心跳的時間
latestHeardTime int64 // 最新的收到leader的AppendEntries RPC(包括heartbeat)
//snapshottedIndex int
}