第一次实现 Raft 的一些笔记

机器翻译 原文.


我用 Python 实现了 Raft 共识算法(分布式算法中的典型代表)。 这个实现相当糟糕! 不过也(算是)正确的。

这里有一些笔记,想分享给同样有兴趣接受这类挑战的人。


回过头来看,下面这些资料对学习 Raft 并正确实现它最有帮助。

我建议先花一个小时左右读论文,然后为 UDP 或 TCP 服务器写个代码框架,让它读取传入的字节并添加到数组中。 接着,我跟着 Eli 的实现,按同样的顺序为自己的 Raft 实现添加功能。

当选举功能看起来能正常运行后,我开始寻找 bug,以及自己对算法理解上的错误。 我会在学生指南、Raft 论文中的图 2 和自己的实现之间来回对照,仔细思考自己的实现在哪些地方与它们相符(或不同)。 我还大量使用 LLM 来审查这些代码,并将上述资料中的内容加入上下文。

对日志复制、持久化等部分,也重复上述过程。


关于实现

我在实现时做了一些简化设计的选择。 以下不分先后:

  1. 每个节点都在单个线程上运行并处理消息
  2. 使用“逻辑时钟”来跟踪系统中的本地“时间”(例如,调用 tick() 并递增各节点自己的计数器,而不是使用系统时间)
  3. 把所有东西都放在一个文件里,让实现变得“混杂”,例如网络解析、存储/持久化、Raft 核心算法,以及用于控制节点本身的工具/命令

2 看起来是一个合理且正确的设计选择(etcd 的实现用的就是逻辑时钟)。 3 可以说更适合学习/教学。 把所有内容都放在一个文件里很方便,既能一眼看到全貌,也能得到一份方便拆解的实现,用来探索哪些抽象最适合这个算法。

在我看来,1 这个选择有点离谱。 它确实让实现简单了很多(不用太操心死锁和节点内部状态的原子更新),但最后得到的东西也不完全是 Raft。 作为第一次实现,这样似乎也没问题。 算法本身已经足够复杂,我想,比起跟互斥锁较劲,你大概更愿意把时间花在调试 Raft 核心算法的逻辑错误上。


我觉得,用这种方式实现 Raft,可以看作一个耗时约 30 小时的项目。 最开始阅读 Raft 论文、查阅相关资料,应该需要几个小时。 我在假期里花了约 3 天完成大部分编码工作,每天写代码大约 6-8 小时。 现有实现还有一些需要打磨和改进的地方(例如修复一些隐蔽的 bug),可能还得再花半天时间。

总的来说,花这些时间来理解支撑着 如此多 基础设施的核心算法之一,还算不错。