接上一个对于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
}