6.824 lab2 raft
Contents
在 lab2 实在是拖的太久了,今天终于通过了所有测试用例,先附上一张测试全部通过的截图。
说实话,做这个lab
还是蛮吃力的,之前对并发编程接触的不多,不管是在写代码,还是在debug
的过程中都遇到了很多的问题,通过全部测试后也是小有成就感。
因为这个lab
要求不要公开代码,所有就不贴代码了。就写一些对raft
的理解和完成这个lab
遇到的一些问题。
关于 Raft
raft
是一个共识算法,是简化版的paxos
, 比paxos
更好理解。raft
解决的是问题就是保证多个副本中日志保持一致。
raft
可以分为三个部分
leader election
log replication
safty
Leader Election
raft
节点的角色有三个:leader
, follower
, candidate
。节点的初始状态是follower
, 每个节点都有一个随机的超时时间,当follower
节点超时时,其状态会变成candidate
, candidate
会向集群中的其他节点发起投票,开始选举,如果拿到多数选票,其状态就变成leader
, 这就完成了leader election
。
Log Replication
raft
的特点是只有leader
做出决策,其他节点只要根据leader
的指令来做就好,所以只有leader
会接受来自客户端的日志,leader
将接受到的日志发送给follower
节点,如果集群中的大多节点都收到了这条日志,日志就可以commit
, 然后apply
。
Safty
raft
如何保证每个节点的日志的顺序一致?
leader
必须存储所有已经提交的日志条目- 有最新日志的节点才能成为
lader
- 只提交任期内的日志条目
实现
参照raft 论
文中的这张图和 lab2 代码的基本骨架实现。用一个goroutine
控制节点状态的变更,利用channel
进行goroutine
间的通信。主要就是实现RequestVote RPC
和AppenEntries RPC
。
2A
2A 部分就是完成leader election
, 完成RequestVote RPC
, 以及不带日志的心跳。这个是之后的基础,一定要多运行几遍,这里如果有bug
的话后面都没法通过。
遇到的坑
heartbeat channel
要设置为有缓冲的,不然会因为 channel 阻塞导致election timeout
- 计算选票是要判断节点状态,不然会导致不应该成为
leader
的candidate
成为leader
- 要通过
race
检查的话,读和写raft
结构体中的数据都要加锁
2B
2B 部分是日志备份的部分,完成AppendEntries RPC
, 完成 2A 后台按照论文中的图实现也不难,基本就是按照 2A 流程走,注意论文图中给的要求写就行。
遇到的坑
- 论文中写的日志索引的初始值是 1,但是
lab
中的测试是由 0 开始的 follower
节点需要返回给 leader 要下一次日志复制开始的索引,这个论文图中的ApendEntriesReply
是没有的,要自己定义
2C
2C 部分是为了保证节点重启数据不丢失,需要将日志,节点状态,任期等信息持久化到磁盘。这个和lab1 mapreduce
中的持久化很类似,我们只要完成对要持久化数据的编解码,然后在节点状态变更的地方调用persist
函数即可。
总结
总体来说,这个lab
还是有难度的,特别是刚开始做的时候,debug
的时候很头疼, 当 2A 部分的测试都通过,race
检测也通过,完成了 2A 部分,后面就简单多了,按照论文说明做的话,基本都会通过测试。完成这个lab
也收获了很多,包括对raft
的理解,对并发程序的debug
, 对goroutine
和channel
的更深入了解,对锁的使用。.. 真的是非常感谢 MIT 能开源这样有意义的lab
。