Lab2 A&&B

写完2A,再写2B,就会发现2A2B是一体的,2B逼着你去重改2A的框架。

主结构

Raft的主结构比较简单,完全依照图二写的:

type Raft struct {
	mu        sync.Mutex          // Lock to protect shared access to this peer's state
	peers     []*labrpc.ClientEnd // RPC end points of all peers 所有peers的RPC端点
	persister *Persister          // Object to hold this peer's persisted state 保存这个peer的持久化状态的对象
	me        int                 // this peer's index into peers[] 在peers数组里的下标(索引index)
	dead      int32               // set by Kill()

	// Your data here (2A, 2B, 2C).
	// Look at the paper's Figure 2 for a description of what
	// state a Raft server must maintain.

	// vote
	currentTerm int
	votedFor int
	//lastResetElectionTime time.Time
	electionTimeOut time.Time
	// entries
	log []Entry
	commitIndex int
	lastApplied int
	// for leader
	nextIndex []int
	matchIndex []int
	// common
	applyCh chan ApplyMsg
	stage int
}

type Entry struct {
	Term int
	Command interface{}
}

然后就是要先完成Leader选举,先补充完Make:

lab的提示里面建议用 sync.Cond 来实现 apply ticker的功能,即让apply ticker 在 rf.lastApplied >= rf.commitIndex时休眠,但是我想着要唤醒这个goroutine是比较麻烦的,要在多数状态机应用之后再通过心跳广播给各个follower,这样感觉费力不讨好。所以我直接用了time.sleep()。不过是以小于心跳的时间间隔来检查ticker,因为没了广播的话,那些重连的机子可能需要非常频繁的去应用日志。

leader 选举

再 candidateElectionTicker中,每100ms检查一次选举超时,如果选举超时,就更改为candidate并重置超时时间,然后发起选举

然后就是去获取选票的函数实现,过程写在注释中:

选票的最后环节就是图二中的两条选举规则,遵循rule 1 2 写就行了

AppendEntries

选举成功之后,要想pass 2A还需要一个AppendEntries来发送心跳,这个比较简单,不用啥规则,leader直接发送就行。

然后2B的内容,就需要先完善Start()里面的逻辑:

start()是Leader一次获取一条命令添加到自己的日志,所以实现起来比较简单。我使用心跳来发送log

在心跳检查的ticker中,每隔50ms进行一次心跳/日志发送

AppendEntries rpc的内容:

日志应用 appliedTicker

实现完日志添加后,就需要应用到状态机:

至此就可以pass 2A和2B的所有test了

Last updated