因为 Paxos 算法过于晦涩难懂且难以实现,Diego Ongaro 提出了一种更易于理解和实现并能等价于 Paxos 算法的共识算法 - Raft 算法。html
由于 Raft 算法清晰易懂愈来愈多的开源项目尝试引入 Raft 算法来解决分布式一致性问题。在分布式存储领域基于 Raft 算法构建的项目百花齐放,欣欣向荣。python
介绍 Raft 算法的文章早已经是汗牛充栋,本文先介绍两个很是优秀的网站:git
The Secret Lives of Data-CN 以图文方式介绍 Raft 算法,是很是好的入门材料。将其阅读完后您大几率已经了解了 Raft 算法,若是您仍有疑问能够回来继续阅读本文。github
既然您已经回来继续阅读,相信您已经了解 Raft 算法中的Leader 选举、日志复制等基本概念, 但仍有部分疑惑。不要紧, 接下来咱们会解决这些问题。算法
Raft Scope 是 Raft 官方提供的互动式演示程序,它展现了 Raft 集群的工做状态。您能够用它模拟节点宕机、心跳超时等各类状况。有了 Raft Scope 咱们能够亲自“动手” 观察 Raft 集群是如何工做、如何处理各类故障的。安全
遗憾的是这个程序几乎没有任何说明很是难以上手。本文接下来将先介绍如何使用 Raft Scope 而后用它模拟几种 Raft 集群工做中会遭遇的典型情况。网络
能够看到 Raft Scope 界面由三部分组成。并发
最下方有两个滑块:上面的是进度条您能够拖动它回看刚刚发生过事件,下面的是变速器滑块越靠左系统运行越慢。分布式
左上角部分是一个由 5 个节点组成的 Raft 集群,每一个圆圈表明集群中的一个节点。点击节点能够看到它的状态。对话框的右下角有一些按钮,咱们能够点击按钮模拟各类情况。咱们直接右键点击节点也能够看到这些按钮网站
这些按钮的功能是:
节点中间的数字是节点当前的任期号(Term), 节点的颜色彷佛一样是用来表示任期的。
节点可能处于 Follower、Candidate 或者 Leader 状态。
S2 处于 Candidate 状态,实心原点表示它如今收到的投票。图中的两个原点表示收到了 S2 和 S4 的投票,这 5 个小圆点和集群中节点的位置是对应的,左下角的小圆点表示 S4, 最上面的小圆点表示 S1。在集群选举过程当中节点外的动态边框表示 Election Timeout。
黑色实心边框表示 S5 是 Leader。Follower 外面的边框表示 HeartBeat 超时倒计时。
右上角的表格表示各节点的日志,每行表示一个节点。
表格最上面的数字是日志的序号(Log Index)。Log Index 是一个自增且连续的 ID,它能够做为一条日志惟一标识。节点中最大的 Log Index 也反映了这个节点的状态机是否与集群一致。
表格里的单元格表示日志项(Entry),其中的数字表示提交日志的任期(Term)。虚线框表示日志还没有提交,实线框表示日志已经提交。
咱们能够点击 leader 节点的 request 按钮来查看向 Raft 集群提交数据的过程。
Raft Scope 启动后会当即进行第一次 Leader 选举,在集群运行过程任何一个 Follower 出现心跳超时都会引起新一轮选举。
咱们能够点击任意一个 Follower 的 time out 按钮模拟心跳超时,随后此 Follower 会发起新一轮选举。
或者咱们能够点击 Leader 的 stop、restart 来模拟 Leader 宕机或者重启,并观察随后的集群选举过程。
比较奇怪的是, Raft Scope 中的 Leader 节点也能够经过点击 time out 来模拟心跳超时,在实际的 Raft 集群中 Leader 节点一般不会对本身进行心跳检测。
Leader 选举的更多介绍能够查看:Leader选举。不过 The Secret Lives of Data 有两处说的可能不太清楚:
这里的选举超时是指新一轮选举开始时,每一个节点随机思考要不要竞选 Leader 的时间,这个时间通常100-到200ms,很是短。
Candidate 发起选举时会将自身任期(Term)+1并向其它全部节点发出 RequestVote 消息,这条消息中包含新任期和 Candidate 节点的最新 Log Index
收到 RequestVote 的节点会进行判断:
def onRequestVote(self, request_vote) if request_vote.term <= self.term: # 若 RequestVote 中的任期小于或等于(<=)当前任期 # 则继续 Follow 当前 Leader 并拒绝给 RequestVote投票 return False if request_vote.log_index < self.log_index: # 若 request_vote 发送者的 log_index 不如本身新,节点也会拒绝给发送者投票 # 这种机制确保了已经提交到集群中的日志不会丢失,即保证 Raft 算法的安全性 return False if self.voted_for is None: # 若在本 term 中当前节点还未投票,则给 request_vote 的发送者投票 self.voted_for = request_vote.sender return True else: return False
如今咱们研究一下 The Secret Lives of Data 没有详细说明的 Follower 超时处理过程。
咱们能够点击任意一个 Follower 的 time out 按钮模拟心跳超时,随后此 Follower 会发起新一轮选举。
根据上文中的 onRequestVote 逻辑,超时的 Follower 的 Log Index 是否与集群中的大多数节点相同决定了此次选举的不一样结果。
首先来看超时 Follower 的 Log Index 与集群中大多数相同的状况:
如今咱们点击 S5 的 time out 按钮,随后咱们看到 S5 发起了一轮投票。由于 5 个节点的 Log Index 是一致的, 因此包含原 Leader 在内的大多数节点都投票给了 S5。
如今 S5 成为了新一任 Leader.
接下来咱们看另一种状况。S5 因为网络问题没有收到带有 Log Entry 1 的心跳包并致使心跳超时,S5 随后会发起一次投票:
因为 S5 的 Log Index 比较小其它节点拒绝投票给他,集群 Leader 和任期不变:
日志复制的介绍您能够查看:日志复制
如今咱们进一步探究日志复制的过程:
这里比较复杂的状况是在第 4 步完成以后 Leader 崩溃。因为此时客户端已经收到了写入成功的回复,因此在选出新的 Leader 以后要继续完成提交。
在 Leader 提交了本身的日志后咱们当即关掉 Leader:
随后集群发起了一次选举,S3 成为新任 Leader:
多是由于 Raft Scope 存在 Bug, S3 本应该当选后当即完成提交工做。可是实际上须要咱们再一次 Request 以后,日志1 和日志 2 才会被一块儿提交。
在 Leader 崩溃时可能会有多个节点近乎同时发现心跳超时并转变为 Candidate 开始选举:
其它节点投票状况多种多样,但只要保证获只有获得过半投票的候选人才能成为 Leader。那么选举结果只有两种可能:
绝对不会选出多个 Leader
Raft 甚至能够在网络分区的状况下正常工做:
在发生网络分区后可能存在 3 种状况:
任意分区中的节点数都不超过一半:这种状况只有集群被分红 3 个或更多分区时才会出现,十分罕见。由于 Leader 选举和 Commit Log 都须要超过一半节点确认才能够进行,在这种状况下 Raft 集群不能正常工做。
leader 所在的分区有超过一半的节点:这种状况视做其它分区中的 Follower 宕机,系统仍然能够继续工做。在分区修复后,Follower 节点会从新与 Leader 同步。
leader 所在分区中节点数不超过一半,但存在节点数超过一半的分区。这种状况最为复杂:
C、D、E 所在的分区节点数超过一半且与原来的 Leader 没法通讯,随后 C、D、E 在心跳超时后会发起新一轮投票选出新的 Leader 并恢复工做。
原领导者 Node B 仍然会认为本身是集群的 Leader,可是因为只能与两个节点通讯(包括本身)没法获得过半节点赞成,因此没法完成日志提交。
在分区修复后 Node B 会收到 Node C 的心跳并发现对方的任期(Term)比本身高,Node B 会放弃 Leader 身份转为 Node C 的 Follower 与它保持同步。
通过本文探讨咱们能够总结一下 Raft 的一些特性:
推荐阅读: