MIT 6.5840 Lab2C"/>
MIT 6.5840 Lab2C
文章目录
- 前言
- 1. 任务
- 2. Lab2C - persist of Raft
- 2.1 分析
- 2.2 编码解码
- 3. 测试
- 4. 总结
前言
如果你的Lab2A和Lab2B完成的非常的好,那么Lab2C会非常轻松,因为只需要在所有需要持久化的部分进行调用即可。反之,找bug会非常痛苦。
1. 任务
如果基于 Raft 的服务器重启,它应该从原来的位置恢复服务。这就要求 Raft 保持持久的状态,以便在重启后继续运行。本文的图 2 提到了哪些状态应该是持久的。
真正的实现会在 Raft 的持久状态发生变化时将其写入磁盘,并在重启后重新启动时从磁盘读取状态。你的实现不会使用磁盘,而是通过 Persister 对象保存和恢复持久化状态(参见 persister.go)。无论谁调用 Raft.Make(),都要提供一个 Persister,它最初保存着 Raft 最近的持久化状态(如果有的话)。Raft 应从该 Persister 初始化其状态,并在每次状态改变时使用它来保存其持久化状态。请使用 Persister 的 ReadRaftState() 和 Save() 方法。
Raft官网
课程实验
2. Lab2C - persist of Raft
2.1 分析
这部分用于解决Raft Crash之后的恢复问题。我们要实现的接口是persist()
、readPersist()
,分别对应着编码(encode)、解码(decode)。光从接口这块和之前比起来,就少了许多了,轻松不少。
以上三个变量就是Persister保存的持久化状态,所以当这三个变量变换的时候,我们就需要调用rf.persist()
。也就是说,当我们宕机后,服务器重新启动就能快速恢复到这三个参数所在的地方。
currentTerm
:第一个参数currentTerm好说,当你恢复后,你肯定还得知道你是哪个Term,而且每个Term只能投一票;votedFor
:这个参数应该是为了保证你宕机恢复后,确认你是否已经投过票了,所以也需要持久化;log[]
:肯定得保存啊,恢复状态就靠log的内容了,如果发现自己term比别人小,那么新的leader就会根据nextIndex给你发送全部的log。
2.2 编码解码
这部分官方都给我们写好了的,直接拿来用就好了。主要是投票和发送心跳日志的地方。
func (rf *Raft) persist() {w := new(bytes.Buffer) // 创建缓冲区,实际上是一个字节切片e := labgob.NewEncoder(w) // 创建一个新的labgob编码器,编码后的内容存放在w中e.Encode(rf.currentTerm)e.Encode(rf.votedFor)e.Encode(rf.log)raftstate := w.Bytes() // 将缓冲区的内容赋值给raftstaterf.persister.Save(raftstate, nil) // 在未实现快照之前,第二个参数设置为nil
}// restore previously persisted state.
func (rf *Raft) readPersist(data []byte) {if data == nil || len(data) < 1 { // bootstrap without any state?return}r := bytes.NewBuffer(data)d := labgob.NewDecoder(r)var(currentTerm intvotedFor intlog []logEntry)if d.Decode(¤tTerm) != nil ||d.Decode(&votedFor) != nil ||d.Decode(&log) != nil {fmt.Println("decode error!")} else {rf.currentTerm = currentTermrf.votedFor = votedForrf.log = log}
}
然后在我们之前写的代码有修改持久化参数的地方加入rf.persist()
就可以了。
3. 测试
在提交之前,最好多次运行测试,并检查每次运行是否都打印出 PASS。
for i in {0..10}; do go test -run 2C -race; done
TestPersist12C,TestPersist22C和TestPersist32C:基础测试,断联peer后重连查看是否正常持久化数据
TestFigure82C:针对Figure.8的情况进行测试,每个操作都要求领导者(如果有)追加log。如果有一个领导者,那么该领导者很快就会失败,概率很高(可能不执行命令),或者过一段时间后崩溃,概率很低(很可能执行命令)。如果活动服务器的数量不足以构成大多数服务器,也许可以启动一个新服务器。新任期内的领导者可能会尝试复制尚未提交的日志条目。
TestUnreliableAgree2C:测试不稳定网络情况下能否达成一致,也就是RPC可能会有1-2秒的延迟或丢失
TestFigure8Unreliable2C:网络不稳定情况下测试Figure.8的情况
TestReliableChurn2C和TestUnreliableChurn2C:分别在稳定和不稳定情况下,每个peer有低概率崩溃重启,在此基础上,进行大量的log追加与提交。
之前一直通过不了TestUnreliableAgree2C
和TestFigure8Unreliable2C
,我把HeartBeat时间改为了50~100ms,就可以了。跑了50次,都PASS了,可能1000遍会有问题吧,但我没测了。如果有网友测了,可以评论区说一下,感谢!
4. 总结
这一节感觉蛮简单的,算是提前完成了任务了。后面就剩下一个日志快照了,成功就在眼前。
今天看到一段小作文,感觉非常符合我今天的心境,分享一下给大家,希望大家也能“追风赶月莫停留,平芜尽处是春山”。
失之东隅,收之桑榆。人生海海,山山而川,不过尔尔。昨日之深渊,今日之潜谈。路虽远,行则将至,事虽难,做则可成。纵然,欲买桂花同载酒,终不似,少年游。亦要追风赶月莫停留,平芜尽处是春山。
回头看:轻舟已过万重山,
向前看:前路漫漫亦灿灿。
更多推荐
MIT 6.5840 Lab2C
发布评论