Raft分布式一致性算法(论文解读和源码实现)

2020-05-13  本文已影响0人  竹羔

前言

建议先对raft论文有一些基本的浏览,然后再看下面的内容。可以结合后面引用的链接去进行更深入的学习。下文提到的章节,指论文的章节

正文

通过领导人的方式,Raft 将一致性问题分解成了三个相对独立的子问题:

除此以外,还有:

图3
领导选举

重点阅读论文的 5.1 以及 5.2,结合 Figure 2 食用。

image.png

只需要两个rpc和两个周期性检查的timer(heartbeat timer和election timer)即可描述领导选举:

对于每一个不同状态的 Raft 节点,他们可能有如下并发的事件:(即复制状态机的行为)

日志复制
image.png image.png
图示:
步骤:

当前leader通过心跳(appendEntities)维护不同节点的nextIndex和matchIndex。

follower节点接收到appendEntity请求时,对比自己的和leader发过来的term和nextIndex。如果自己的小,就返回false,让leader降低nextIndex。直到leader发过来的日志就是自己缺失的那部分,直接删掉follower节点的matchIndex之后的数据,添加leader发过来的自己缺失的数据。

leader节点接受到appendEntity的返回后:

如果是成功的返回:维护该follower的matchIndex和nextIndex,查看当前是否绝大多数follower节点已复制(通过查看leader维护的matchIndex数组来判断),如果大多数已复制,即表示该日志项已提交,下一次心跳的AppendEntity会使得各follower的commitIndex也得到更新,即各follower节点的该日志项也处于已提交状态。;

如果是失败的返回,降低对应follower的nextIndex,然后直接对该follower重新发送appendEntity

image.png image.png image.png image.png

此时该复制状态机的状态就保持了一致(获得共识)。

安全性

详见论文第五节

描述了 Raft 算法是如何选举和复制日志的。然而,到目前为止描述的机制并不能充分的保证每一个状态机会按照相同的顺序执行相同的指令。

成员关系变化

详见论文第6小节。

image.png

两阶段变更的方式:

第一阶段:共同一致(过渡阶段)

第二阶段:切换到新配置

日志压缩

详见论文第7小节

在实际的系统中,Raft 产生的日志在持续的正常操作中不断增长,但不可以无限的增长下去。

快照(snapshot)是最简单的压缩方式。在快照中,全部的当前系统状态都被写入到快照中,存储到持久化的存储中,然后在那个时刻之前的全部日志都可以被丢弃。

Raft快照基本思想:

异常情况分析

image.png

上图的任意一步,任意一个节点都可以宕机。也可以保证数据一致性。。

follower或者candidate节点崩溃
客户端请求的数据到达leader前,leader崩溃

等待集群重新选举。且客户端重新请求。

客户端数据已到达leader,但未复制到绝大多数follower(即处于uncommited),leader崩溃

uncommited数据无意义。等待集群重新选举。且客户端重新请求。

客户端数据已到达leader,已复制到绝大多数follower(处于commited),leader崩溃

等待集群重新选举。且客户端重新请求。但是该不应该继续保存在复制状态机上。(不然就造成重复提交)。因此需要raft服务能内部去重(具备幂等性,实现线性化语义),论文上是第八节(client interaction)中提到一个解决方案就是:

客户端对于每一条指令都赋予一个唯一的序列号。然后,状态机跟踪每条指令最新的序列号和相应的响应。如果接收到一条指令,它的序列号已经被执行了,那么就立即返回结果,而不重新执行指令。

网络分区,导致双leader

客户端向那个不具备绝大多数节点的leader请求,肯定是超时的。

客户端只有向具备绝大多数节点的leader请求才能正常返回。

多说一下,分区后的数据恢复:

不正常的leader网络恢复,接收到正常leader的appendEntity后降级为follower,进行日志复制过程。

代码实现raft

自己实现过一个简单的raft,包含领导选举,日志复制和安全性,未实现日志压缩,成员关系变化。

思路:

一个节点使用了一个go routine模拟一个状态机,两个timer,两个rpc(使用go的net/rpc内置包)。

但是发现测试特别困难。仅仅根据论文描述写了代码,也简单的打印测试,基本是可用的,可以选举出来leader,同步数据,但是异常边界情况很难模拟出来然后测试。
代码如下,实现不完整:
https://github.com/theoneLee/go-raft-demo

todo

后面发现raft.github.io上有一个mit分布式系统的课程,里面包含了raft的一个实现,且附带完善的测试用例,准备好好看课程,然后实现这块内容。

参考

https://ramcloud.atlassian.net/wiki/download/attachments/6586375/raft.pdf

https://raft.github.io/

http://thesecretlivesofdata.com/raft/

http://nil.csail.mit.edu/6.824/2020/index.html

上一篇 下一篇

猜你喜欢

热点阅读