lab3比lab2更加自由一点,主要是没有论文参照以及太多的资料可以查,而且调试难度也比较大。lab3A主要是实现基于Raft的kv server,client给server发送请求,然后server通过底层Raft来保持log的一致性,操作存储在内存中。lab3B主要是实现给内存快照,然后持久化到persister中。
我的代码:MIT6.824
lab3A
client
首先是实现client的操作,我们可以先通过查看test_test.go来了解测试过程,首先测试会新建几个server和client,然后调用调用client的请求操作,client的请求操作会通过RPC调用server端的实现。
主要是实现client端的Get和PutAppend方法,这个其实很简单,我们只需要在两个方法中,无限循环的尝试给server端发送RPC请求,如果成功就返回,不成功就继续发送。
为了防止在网络不稳定等情况下,server已经执行了操作,但是reply会没有回到client端,我们需要维护操作的幂等性,也就是判重。一个简单的方法是给每次client端发送的请求都加上一个ClientId和Seq,其中前者是每个client都有的唯一的64位编号,后者是随着每个新的请求递增的请求序列号。
由于我们测试的情况下,一个client如果某个请求还没有成功执行,那么它就会一直重复当前这个请求,所以我们只需要在server端记录一下对于每个client,已经收到的最大的序列号是多少即可。
server
server端主要是需要接收client发来的请求,然后执行,执行过程主要是把log发送到底层的Raft服务中,然后通过Raft来commit这个请求,然后在server端从channel中获取请求,然后应用到内存中,然后通知client端操作已经完成或者没有去完成。
整个过程是同步的,开始执行请求之后就必须等请求执行完成才能回复给client,所以可以通过Raft的Start函数返回请求在log中的index,对于每个index创建一个channel来接收执行完成的消息,接收到了之后,知道是完成操作了,然后回复给client
需要单独开一个goroutine来监视apply channel,一旦底层的Raft commit一个,就立马执行一个。
lab3B
snapshot
snapshot其实并不复杂,需要做的几个点就是:
- 启动
server的时候,需要读取snapshot,并且要在底层的Raft开始运行之前。 - 当
Raft state也就是log以及其他两个参数超过一定限度的时候,server要将此时的内存状态保存为snapshot - 底层的
Raft Leader在发送心跳的时候发现和Follower的log不匹配的点,在它现有的log中不存在(已经被存入snapshot),Leader就需要把AppendEntries RPC变为InstallSnapshot RPC,同时附带心跳效果。 InstallSnapshot RPC主要是截断log,然后其他判断和其他RPC相同,然后往apply channel里发送一个带snapshot的消息,当server端取出执行的时候,就是执行加载这个snapshot的操作了,而不是client的请求。
本文详细介绍了MIT6.824课程中Lab3的任务实现过程,包括基于Raft算法的kvserver设计与实现,以及内存快照的持久化策略。文章深入探讨了客户端与服务器端的交互机制,特别关注于如何确保操作的一致性和幂等性。

1896

被折叠的 条评论
为什么被折叠?



