本课程中要实现的raft,需要对外提供以下接口
// 创建一个新的 Raft 实例
rf := Make(peers, me, persister, applyCh)
// 就一个新的日志条目达成一致
rf.Start(command interface{}) (index, term, isleader)
// 询问 Raft 实例其当前的 term,以及是否自认为 Leader(但实际由于网络分区的发生,可能并不是)
rf.GetState() (term, isLeader)
// 每次有日志条目提交时,每个 Raft 实例需要向该 channel 发送一条 apply 消息
type ApplyMsg
RPC
我们需要定义请求参数和返回值结构体,自己实现Client端的发送逻辑,实现Server侧的回调函数
Raft 实例包含三个工作流
1 领导选举,时钟触发
2 日志同步 时钟触发
3 日志应用 事件触发
将所有工作流 命名为 xxxTicker
领导选举流程electionTicker
每个一段时间重置计时,检查记录时间点与当前时间差值是否超过选举时间间隔,决定进入选举流程 startElection
选举流程就是向其他所有Peer发送Request Vote RPC请求,看是否获得半数以上选票当选,这个过程需要为每个RPC请求启动一个goroutine,因为耗时不确定,请求可能堵塞或丢失
日志同步流程replicationTicker
日志同步和领导选举工作流的生命周期不同,领导选举工作流会持续在整个raft实例的存活周期中,而日志同步工作流只会持续它当选为领导的任期term中,如果再次当选,启动的是一个新的工作流
领导选举的超时间隔是在一个小范围内随机的,而日志同步的超时间隔是固定的
日志应用applicationTicker
当条件满足时,遍历相关日志,逐个apply,将msg发送到channel内
状态机
抽象出三个函数,becomeFollower,becomeCandidate,becomeLeader,需要上锁
PartA 不带日志比较的领导者选举
PartB 日志同步,完善领导选举
PartC 状态持久化,将所有影响逻辑的状态外存
PartD 日志压缩