这门课的关键就是Raft,Lab2 A就能看出相对于MapReduce,需要阅读的材料和理解更加的多,也用到了一些平时开发没怎么注意到的思路。

Raft

Leader选举

实验的开始是实现选举机制,首先需要理解选举的流程。在raft.go文件中实际上屏蔽了一些底层信息,比如加入Server,维护Server的状态等相关信息,这部分内容实际上是在config.go文件中的。既然不考虑一些底层的具体实现,只需要考虑Raft的算法本身。

TestInitialElection2A中创建了3个Server,也就是说1个会成为Leader,其他两个应该是处于Follower的状态。首先每个Server是处于Follower状态,但这时候的Follower并没有follow任何Leader,Follower有一个属于自己的随机timeout,也就是Election Timeout,通常timeout设置为150~300ms。当该timeout结束后,这个Server就会从Follower切换到Candidate状态,同时会开始进入election term。这部分阶段做了一些工作,其中最主要的工作应该算是 拉票 (RequestVote) 。选举阶段,转为Candidate状态的节点执行了以下几个操作:

  • 增加currentTerm
  • 给自己投票
  • 重置选举计时器
  • 发送RequestVote给其他节点

当该节点收到过半的赞成票时,即可从Candidate转为Leader。

RequestVote

这部分内容参考论文 Figure 2 上的部分进行编码即可,这里要注意的是,除了 RequestVote RPC 里提到的规则,所有 Servers 都需要遵循一个规则

If RPC request or response contains term T > currentTerm: set currentTerm = T, convert to follower

因此 RequestVote 函数可以初步实现。

func (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) {
	// Your code here (2A, 2B).

	rf.mu.Lock()
	defer rf.mu.Unlock()

	if args.Term < rf.currentTerm {
		reply.VoteGranted = false
		return
	}

	if args.Term > rf.currentTerm {
		rf.currentTerm = args.Term
		rf.state = FOLLOWER
		rf.votedFor = args.CandidateId
	}

	if args.Term == rf.currentTerm {
		if rf.votedFor == args.CandidateId || rf.votedFor == -1 {
			rf.votedFor = args.CandidateId
			reply.VoteGranted = true
			rf.elapseReset <- struct{}{}
		}
	}

	reply.Term = rf.currentTerm
}