Raft 是解决分布式一致性的算法之一。它本身是非拜占庭算法,无法容忍集群内的恶意节点,同时最高容忍不超过一般的节点故障。
Raft 算法的基本原理是通过在集群中选出 Leader 节点,然后 Leader 负责提交日志,其它节点负责同步日志。最后每个节点通过日志计算出当前的状态,从而保证了状态的一致性。
复制状态机模型
复制状态机模型是分布式系统常用的一种模型。其特点是节点之间同步日志,然后根据日志计算出状态机的状态。
节点之间通过保证日志的一致性从而保证了状态机的一致性。
选主算法
Raft 中一个节点有三种角色:Leader, Follwer 和 Candidate。
初次选主: 集群上线后每个节点都会随机等待一段时间。然后成为 Candidate。Candidate 会向其它节点发送投票请求(RequestVote)。若Candidate 收到了半数以上的投票则成为 Leader,其它节点称为 Follower。
再次选主 当 Leader 节点宕机后,其他节点由于超时未收到 Leader 的心跳包,会重新选主。这里由于每个节点的定时器时间是不一样的,因此不会出现所有节点同时称为 Candidate 的情况。 另一种情况是由于网络问题导致节点出现了分区。但是由于需要半数以上的节点投票,因此节点数较少的分区不会选出 Leader。
Leader 不会主动放弃领导地位。 |
由于 Leader 会不断轮换,因此 Raft 将每个 Leader 领导的时间段称为任期(Term)。每个任期都有一个唯一的编号,任期的 ID 是单调递增的。
若 Leader 收到了一个比自己 term 更大的编号,则会放弃领导地位,同时更新 term。
日志同步
Raft 中只有 Leader 才有权限向其它节点发送日志。Leader 日志同步是两阶段提交协议,具体而言:
如果某些 Follower 没有成功提交日志。Leader 会不断重试 AppendEntries RPC 直到所有 Followers 都储存了这些日志。
日志由日志编号、任期号和命令组成。Leader 中使用两个数组 matchIndex 和 nextIndex 分别记录每个 Follower 已经同步了的日志和下一个需要同步的日志。
在同步日志的过程中,Leader 保证:
只会对(自己的)日志进行追加操作。
如果两个日志的 index 和 term 相同,则这两个日志相同。
如果两个日志相同,则它们之前的日志都相同。
已经 Commit 的消息,一定存在于后续的 Leader 节点上。
若 Follower 日志不同,则强制覆盖。
由于 [投票规则],且多数节点都存在相同的日志,因此 Raft 能够保证选出来的 Leader 日志总是最新的。