初步实现raft选举,保证一个任期仅有一个leader,在leader宕机后其他follower会自动拉票竞选成为leader。
大致思路
- 最初所有服务器都为follower,并且初始化一个选举计时器(300~400ms)。当选举计时器超时,follower会成为candidate进行竞选投票,如果candidate得到大部分的选票,则会成为leader
- leader会定期发送心跳包以及重置自己的选举计时器,防止新的一轮选举在自己未宕机的情况下开始。
RPC部分
//请求投票:候选者想要成为leader
func (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply)
//复制log 或 心跳包(log为空时为心跳包)
func (rf *Raft) AppendEntries(args *AppendEntriesArgs, reply *AppendEntriesReply)
注意:rpc会被多个服务器调用,需要全程加锁
服务器接收到任何任期高于自己的rpc后,会更新自己的任期。
RequestVote:
1.向当前服务器请求投票,如果candidate的任期<当前服务器的任期,则会拒绝投票。
2.在收到投票请求后,自身转变为follower,并且重置自己的选举超时时间。
3.如果candidate的任期合法并且leader在最新任期没有进行投票,则投票给candidate。
AppendEntries:
1.若leader任期<当前服务器任期,则直接返回
2.服务器在接收到心跳包时,会转变成follower,并且重置自己的选举计时器。
实现细节
- 成为候选人:1.增加term 2.选举自己 3.重置选举计时器 4.并行发送投票请求
- 并行向其他服务器请求投票:
for i := 0; i < len(rf.peers); i++ {
reply := RequestVoteReply{0, false}
if i != rf.me {
go func(i int, args RequestVoteArgs, reply RequestVoteReply, rf *Raft) {
if rf.status == "candidate" {
rf.sendRequestVote(i, &args, &reply) //发送请求投票rpc
}
if reply.VoteGranted == true {
//获得投票
rf.SuccessVoteNum++
} else {
//未获取投票
}
ch <- reply
finishVoteNum++
}(i, args, reply, rf)
}
}
//完成全部投票或者得到的投票数过半时停止等待
for {
select {
case <-ch:
if finishVoteNum == len(rf.peers)-1 || rf.SuccessVoteNum > len(rf.peers)/2 {
goto checkTerm //这里只有goto+标签和break+标签能够退出
}
}
}
checkTerm: