raft 论文(背全文)
Lab 2A
leader election 和 voted 不考虑日志的逻辑。应实现以下内容:
- 初始化集群和两个定时器:HeartBeatTimer(stable),ElectionTimer(random).
- 开启ticker协程,select 监控计时器事件。
- headtbeat:leader 定时广播 AppendEntriesReq,维护集群状态,。节点收到AppendEntriesReq 时 ,根据req.Term 判断是否真实leader做出相应的反应。
- election:当前节点长期未收到心跳,可能是leader节点挂断等原因,当前节点进入 Candidate State, 广播 RequestVote。收到RequestVote对节点做出相应的判断resp
注意几点:
- 任何接收节点rpc请求应根据req.Term 更新自己和resp状态可以及时更新掉线节点state。
- timer随机时间加锁
- 收到任何resp.Term > rf.currentTerm 时 当前节点应变为 Follower State
Lab 2B
复制每个节点之间的日志,确保当集群中出现脱机时,每个节点之间的日志仍然是准确和同步的。主要实现内容:
- leader 要保证:和各节点日志的匹配状态,当广播心跳时,根据匹配状态发送相应日志,并根据返回结果,判断是否追加日志成功做出相应反应。
- follower 保证:根据心跳同步当前节点的日志 ,判断过程中的这一点leader是否过期,以及你是否有脏日志。leader 日志的持久性index,更新你的持久性index
- 保证各节点:commit Index,apply index同步。可以提交日志和持久日志的位置。
注意几点
- commit 和 apply 同步可以打开协程监控。最暴力的方法是sleep检查是否可以同步一段时间。更优雅的方法是使用同步变量:applier 协程wait 。每当发现节点 commit 被更新时,Signal唤醒即可。
- 广播心跳时间:1.StableTimer. 2.从client收到新日志时.3.发现心跳follower当有脏日志时,节点应重新发送心跳(
因为在这里等待心跳是相应的阻塞,所以打开一个新的协程来异步发送。如果发送在这里被阻塞,集群将无法长期选举leader暂时不明白为什么。 - 由于上述广播机会种类繁多,可能会产生leader由于同一节点发送多个相同的心跳包,follower同步日志覆盖同步,所以会回复这些心跳包response.Success = True。那么此时leader如果直接更新节点的匹配状态nextIndex[peer] = len(req.Entries) 的话 会增加更多,导致一些列数组越界。这里的解决方法主要有两种:1.将更新匹配状态也改为覆盖更新:nextIndex[peer] = req.PreIndex len(Entries) 1。2. 避免将相同的心跳包发送到同一节点,可以额外存储一些状态,但会导致编码复杂。所以这里选第一个。
这里可以额外总结一下 日志从client发到leader ,到同步到follower基本流程:
- client 向 leader 发送command
- leader 与自己同步log中
- leader 心跳包发送到每个节点。
- 各节点收到心跳包并同步到他们自己的log中
- 返回同步结果
- leader根据同步结果,更新每个节点的日志状态(match, next),以及自己的commitIndex
- 根据commitIndex 更新applyIndex持久化。
- 等待下一次client的command
可见,如果按顺序完全执行上述流程,效率很低。 所以需要对一部分进行异步的优化: 1.client 向leader发送command 2.leader与自己同步log中 3.异步发送心跳包。继续接受client的command 4.处理response时,发现commit更新后,唤醒持久化applier协程,并将结果返回客户端。
(玩了几天游戏,进度有点慢。接下来,记录每一个lab的时间)
Lab 2C (11.18~11.20)
11.18 又玩了一天游戏 咕咕咕 11.19 玩游戏,鸽子 11.20 Complete
在重启机器时,可以持续一些信息以恢复信息,并使用官方界面。我不明白为什么要单独放一个lab
想过所有test的话这里要 处理日志冲突case做一些优化:发现冲突不能简单的做nextIndex - 1 这篇论文也提到了。
。。没什么好注意的。 今天就到这里QAQ,明天继续