引言
这篇文章我们来进行PartC的编写,这个相比于PartB来说就是轻松太多了,但是仍需要我们头脑清楚。首先来看看我们的任务是什么。
Implement persistence by first adding code to serialize any state that needs persisting in persist(), and to unserialize that same state in readPersist(). You now need to determine at what points in the Raft protocol your servers are required to persist their state, and insert calls to persist() in those places. Once this code is complete, you should pass the remaining tests. You may want to first try and pass the “basic persistence” test (go test -run ‘TestPersist1$’), and then tackle the remaining ones.
通过添加代码去序列化那些需要在persist()函数中需要保存的状态达到实现持久化状态的作用,在readPersist()函数中反序列化相同的状态。你们需要觉得在Raft协议栈中你们的完全在哪些关键点需要持久化它们的状态,然后在这些代码插入persist()函数。一旦这些代码完成,你们就可以通过剩下的测试了。你们也许想第一个尝试通过"basic persistence" 测试(
go test -run 'TestPersist1$'
),然后解决剩下的其他测试。
其实PartC的测试也是比较多的,有八个测试样例,但是如果我们PartB考虑的比较周全的话这里不会出什么大问题。还有一点就是go test -run 'TestPersist1$'
通过的是PartC,八个测试中的第一个,如果使用上述命令过了先不要开心。。
思路
我们的任务就是填充persist
和readPersist
函数,但是我们该做些什么呢?其实在提示中已经告诉我们,可以参考Figure2,那么我们就来参考一下,注意我找到的那个翻译里面是忽略了Figure2里面的这个信息点的,我们来看看吧:
也就是说只要我们持久化这三个参数我们就可以通过做到在节点重启后做到重新复盘宕机前的状况。为什么呢?首先Term
不必多说,每个Term里面每个节点只能投一票,这保证了算法的正确性。votedFor
可以使follower在一次选举中状态不丢失,其实现在我认为只需要记录在此Term中是否投过票即可,至少在我的实现中并没有用上,知道的朋友可以在评论区解答我的疑惑。还有就是log了,这不必多说了。其实有一点很有意思,就是我们在发送日志的时候会为每个节点维护一项nextindex
,记录下一个发送给这个节点的日志index,但是这里确没有,为什么呢?我的理解是这样的,如果我们没有记录的nextindex话,分两种情况,一次选举超时之内,和一次选举超时之外。前者会导致leader宕机恢复以后的第一个广播日志发送全部的日志项,因为nextindex为初始值,此时follower会按照args.nextindex,覆盖掉前面的全部日志,虽然低效,但是正确性是可以保证的,需要优化吗或因此记录nextindex,我认为不必,因为这种概率总是很小的,而且磁盘操作又很昂贵。还有一种就是一次选举超时之外,这个时候已经有了新的leader,等待新leader的心跳包来,老leader发现自己的Term低于新leader,会自动变为follower,这样正确性就保证了。
至此,我们的任务就是在所有改变了上面三个成员变量的地方调用rf.persist()
就可以了。当然persist
函数也是要写的,不过模板已经写好了,直接拿来用就可以了。
func (rf *Raft) persist() {
// Your code here.
// Example:
// w := new(bytes.Buffer)
// e := gob.NewEncoder(w)
// e.Encode(rf.xxx)
// e.Encode(rf.yyy)
// data := w.Bytes()
// rf.persister.SaveRaftState(data)
w := new(bytes.Buffer)
e := gob.NewEncoder(w)
e.Encode(rf.currentTerm)
e.Encode(rf.votedFor)
e.Encode(rf.logs)
data := w.Bytes()
rf.persister.SaveRaftState(data)
}
至此 lab2 就完成了。
虽然完成了全部,但是其实还是有bug的,因为分布式代码真的很难排错,而且bug还是随机出现的,这使得真的很难完成一个完美的共识算法,全部的代码我跑了大概20次,出现了1次错误的情况。在持久化的测试中出现了协议错误。所以还是不到火候。全部代码的链接在这里
总结
lab2到了这里就完成了,收获还是很多的,以前以为自己懂了Raft,做了这个才知道还差的远,直到现在,Raft也是学懂了最基础的部分,很多部分还没有真正学透,但在我看来还是就此为止了,毕竟不是搞这个的。快开学了,最近该总结下这个特殊的假期里面学了哪些东西,这两天就不再看6.824了,过上一阵再搞。话说昨天还说今天不看电脑的,骗人是小狗。汪汪汪。