概述

Lab2B需要我们完善rf.Start(..)这个函数.这个函数接受一条command作为入参,如果当前节点rfleader, 那么就会开始达成共识的过程.

如果当前节点是leader,rf.Start()会把命令压入自身日志,立刻返回index。调用者拿到了一个index,这并不意味着任务完成了。因为raft只保证committed的日志会被执行,所以调用者需要继续监听applyChan。只有applyChan中出来了一条index相同,内容也相同的日志,此时调用方才能执行命令。
如果出来了一条index相同但是内容不同的日志,表示之前提交的那条日志被覆盖掉了,需要调用方告知客户端重试。

测试

有如下测试场景:

  1. basic agreement, 没有出错的情况下达成一致。
  2. agreement despite follower disconnection ...
  3. no agreement if too many followers disconnect ...
  4. concurrent Start()s ...
  5. rejoin of partitioned leader ...
  6. leader backs up quickly over incorrect follower
  7. RPC counts aren't too high ,rpc间隔大概为100ms即可

重构代码

笔者原来的程序通过向channel发送信号来进行状态变更、重置计时器,代码冗长凌乱,难以调试。于是笔者花时间重写了代码,总体而言是大量减少goroutine,select和channel的使用,改为同步调用。

新程序大体如下:

  • 用状态机模式实现:raft节点运行一个无限循环,在循环内跟着自身状态进入leader(), candidate(), follower()三个处理函数,每一个都有不同的行为,在恰当时候检查自身状态是否变化。
  • 发送HB的模式改为leader周期性创建n-1个goroutine,一个goroutine负责发送一个心跳。
  • 定时器用一个成员Timer,而不是使用一个单独的、常驻的goroutine负责计时,这样可以在普通函数中用Timer.Stop(), Timer.Reset(..)来控制计时.
  • 用原子类型,减少锁的使用。
  • 认真阅读论文,按论文要求处理细节。
  • 对于论文没有提到的细节,参考student's guide的实现,例如如何处理过时rpc,何时重置计时器。

重构后的代码减少了200+行,而且顺利地通过了Lab2A,Lab2B(连续100次通过)。尽量减少goroutine,select和chan的使用,方便其他语言参考。

参考资料


Tsukami
9 声望9 粉丝

语雀: [链接]


« 上一篇
MIT6.824-Lab2A
下一篇 »
MIT6.824-Lab2C