分布式共识算法 (一) 背景html
分布式共识算法 (三) Raft算法github
Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但Raft更加容易理解和实践,在工程领域的重要性毋庸置疑。注:本文是在研读Raft算法论文后写出,因原版论文太长,故提炼了一下重点,方便你们快速掌握。安全
区别于通常一致性算法,Raft算法的特性以下:服务器
强Leader:Raft 使用一种更强的领导形式。日志只从Leader流向servers。这种方式简化了对复制日志的管理而且使得 Raft 算法更加易于理解。网络
Leader选举:Raft 算法使用一个随机计时器来选举Leader。这种方式只是在任何一致性算法都必须实现的心跳机制上增长了一点机制,这在解决冲突的时候会更加简单快捷。并发
成员关系调整:Raft 使用一种共同一致的方法来处理集群成员变换的问题,处于调整过程当中的两种不一样的配置集群中大多数机器会有重叠,这就使得集群在成员变换的时候依然能够继续工做。分布式
共识算法一般出如今复制状态机的上下文中。在这种方法中,一个server集群上的多个State Machine计算相同状态的相同副本,而且在一些机器宕掉的状况下也能够继续运行。复制状态机在分布式系统中被用于解决不少容错的问题。复制状态机一般都是基于复制日志实现的,结构以下图:post
如上图,每个server存储一个包含一系列指令的log,而且State Machine顺序执行log中的指令。每个Log都按照相同的顺序包含相同的指令,因此每个server都执行相同的指令序列。由于每一个状态机(state machines)都是肯定的,每一次执行操做都产生相同的状态和一样的结果序列。
Replicated state machines 流程:
为了让算法更加可理解,Raft的做者作了2方面的努力:
raft算法中每一个server可能存在3种身份:Leader领导者、Candidate候选者、Follower追随者。
如上图,一个Follower没有收到来自Leader的心跳超时后,进入候选者Candidate身份,开始尝试参与选举。当收到超过1/2的选票时就变成Leader,并发送心跳给follower.
如上图,Raft 把时间分割成任意长度的任期(term)。任期用连续的整数标记。每一段任期(term)从一次选举(election)开始,一个或者多个候选人尝试成为领导者。若是一个候选人赢得选举,而后他就在接下来的任期内充当领导人的职责。在某些状况下,会形成选票的割裂spilt(达不到大部分的票)。在这种状况下,这一任期会以没有领导人结束;一个新的任期(和一次新的选举)会很快从新开始。Raft 保证了在一个给定的任期内,最多只有一个领导者。
Raft设定了5个原则,这些原则在任什么时候候有效!!
Raft 将一致性问题分解成了三个相对独立的子问题:领导者选举(Leader election)、日志复制(Log replication)、安全性(Safety)。
Raft 使用一种"心跳机制"来触发领导人选举。当服务器程序启动时,他们都是跟随者身份。只要从领导人或者候选者处接收到有效的 RPCs,这个服务器节点保持着跟随者状态。Leader周期性的向全部Follower发送心跳包来维持本身的权威。若是一个跟随者在一段时间里没有接收到任何消息,也就是选举超时,那么他就会认为系统中没有可用的领导者,而且发起选举以选出新的领导者。
要开始一次选举过程,Follower先要增长本身的当前任期(term)号而且转换到Candidate状态。而后他会并行的向集群中的其余服务器节点发送请求投票的 RPCs 来给本身投票。候选人会继续保持着当前状态直到如下三件事情之一发生:
Raft 算法使用随机选举超时(例如 150-300 毫秒)的方法来确保不多会发生选票瓜分的状况,就算发生也能很快的解决。每个候选人在开始选举时,会重置随机的选举超时时间,而后在超时时间内等待投票的结果;这样减小了在新的选举中另外的选票spilt的可能性。
一旦一个Leader被选举出来,他就开始为客户端提供服务。客户端的每个请求都包含一条被复制状态机执行的指令。leader把这条指令添加进log做为一个新entry,而后并行给其余的服务器发AppendEntries RPCs ,让他们复制这条entry。当这条log entry被安全的复制,leader会把entry添加进状态机中而后把执行的结果返回给client。若是follower崩溃或者运行缓慢,再或者网络丢包,leader会不断的重复尝试AppendEntries RPCs (尽管已经回复了客户端)直到全部的follower都最终存储了全部的log entry。
如上图,每个log entry存储一条状态机指令和从Leader收到这条指令时的任期号(term number)。每一条log entry都有一个index来代表它在日志中的位置。
1.Leader一旦把建立的Log entry复制到大多数的server上的时候,log entry就会被提交commited(例如上图中的条目 7)。Raft 算法保证全部已提交的日志条目都是持久化的而且最终会被全部可用的状态机执行。
2.同时,领导人的日志中以前的全部日志条目也都会被提交,包括由其余领导人建立的条目。
3.一旦follower得知一个log entry已提交,follower就会把这个log entry添加进本地状态机
Leader崩溃后可能致使日志不一致,以下图:
如上图,当一个领导人成功当选时,跟随者多是任何状况(a-f)。每个盒子表示是一个log entry;里面的数字表示任期号。跟随者可能会缺乏一些log entry(a-b),可能会有一些未被提交的log entry(c-d),或者两种状况都存在(e-f)。例如,场景 f 可能会这样发生,某服务器在任期 2 的时候是领导人,已附加了一些log entry到本身的日志中,但在提交以前就崩溃了;很快这个机器就被重启了,在任期 3 从新被选为领导人,而且又增长了一些log entry到本身的日志中;在任期 2 和任期 3 的日志被提交以前,这个服务器又宕机了,而且在接下来的几个任期里一直处于宕机状态。
解决方案:
要使得follower的日志和本身一致,leader必须找到最后二者达成一致的地方,而后删除从那个点以后的全部log entry,发送本身的日志给follower。全部的这些操做都在进行AppendEntries RPCs 的一致性检查时完成。leader针对每个跟随者维护了一个 nextIndex,这表示下一个须要发送给跟随者的log entry的index。当一个领导人刚得到权力的时候,他初始化全部的 nextIndex 值为本身的最后一条日志的index加1。若是一个跟随者的日志和领导人不一致,那么在下一次的附加日志 RPC 时的一致性检查就会失败。在被跟随者拒绝以后,领导人就会减少 nextIndex 值并进行重试。最终 nextIndex 会在某个位置使得领导人和跟随者的日志达成一致。当这种状况发生,AppendEntries RPC 就会成功,这时就会把跟随者冲突的日志条目所有删除而且加上领导人的日志。一旦AppendEntries RPC 成功,那么跟随者的日志就会和领导人保持一致,而且在接下来的任期里一直继续保持。
Raft使用voting process来防止那些log不包含所有committed entry的candidate赢得选举。candidate为了赢得选举必须和cluster的majority进行交互,这意味着每一个committed entry必须都在其中的一个majority存在。若是一个candidate的log至少和任何majority中的log保持up-to-date("up-to-date"将在下文精肯定义),那么它就包含了全部committed entry。RequestVote RPC实现了这一约束:RPC中包含了candidate的log信息,若是它本身的log比该candidate的log更新,voter会拒绝投票。
如何比较哪一个新(up-to-date)?
Raft经过比较log中last entry的index和term来肯定两个log哪一个更up-to-date。若是两个log的last entry有不一样的term,那么拥有较大term的那个log更up-to-date。若是两个log以相同的term结束,那么哪一个log更长就更up-to-date。
如上图,展现了为何领导人没法决定对老任期号的日志条目进行提交。在 (a) 中,S1 是领导者,部分的复制了索引位置 2 的日志条目。在 (b) 中,S1 崩溃了,而后 S5 在任期 3 里经过 S三、S4 和本身的选票赢得选举,而后从客户端接收了一条不同的日志条目放在了索引 2 处。而后到 (c),S5 又崩溃了;S1 从新启动,选举成功,开始复制日志。在这时,来自任期 2 的那条日志已经被复制到了集群中的大多数机器上,可是尚未被提交。若是 S1 在 (d) 中又崩溃了,S5 能够从新被选举成功(经过来自 S2,S3 和 S4 的选票),而后覆盖了他们在索引 2 处的日志。反之,若是在崩溃以前,S1 把本身主导的新任期里产生的日志条目复制到了大多数机器上,就如 (e) 中那样,那么在后面任期里面这些新的日志条目就会被提交(由于S5 就不可能选举成功)。 这样在同一时刻就同时保证了,以前的全部老的日志条目就会被提交。
解决方案:
为了防止这样问题的发生,Raft不会经过计算备份的数目,来提交以前term的log entry。只有leader的当前term的log entry才会计算备份数并committed;一旦当前term的entry以这种方式被committed了,那么以前的全部entry都将由于Log Matching Property而被间接committed。
这里原论文是对Raft5个原则的第四个“领导者完整性(Leader Completeness Property)”的论证,咱们并不关心这个。
若是跟随者或者候选人崩溃了,那么后续发送给他们的 RPCs 都会失败。Raft 就会无限的重试;若是崩溃的机器重启了,就成功了。Raft 的 RPCs 都是幂等的,因此这样重试不会形成任何问题。例如一个跟随者若是收到附加日志请求可是他已经包含了这一日志,那么他就会直接忽略这个新的请求。
咱们对于Raft的一个要求是,它的安全性不能依赖于时间:系统不会由于有些事件发生地比预期慢了或快了而产生错误的结果。然而,可用性(系统及时响应client的能力)将不可避免地依赖于时间。好比,由于server崩溃形成的信息交换的时间比一般状况下来得长,candidate就不能停留足够长的时间来赢得election;而没有一个稳定的leader,Raft将不能进一步执行。
leader election是Raft中时间起最重要做用的地方。当系统知足如下的timing requirement的时候,Raft就可以选举而且维护一个稳定的leader:
广播时间(broadcastTime) << 选举超时时间(electionTimeout) << 平均故障间隔时间(MTBF)
在这个不等式中,broadcastTime是server并行地向集群中的每一个server发送RPC而且收到回复的平均时间;electionTimeout就是选举超时;MTBF是单个server发生故障的时间间隔。broadcastTime必须比electionTimeout小几个数量级,这样leader就能可靠地发送heartbeat message从而防止follower开始选举;经过随机化的方法肯定electionTimeout,该不等式又让split vote不太可能出现。electionTimeout必须比MTBF小几个数量级,从而让系统能稳定运行。当leader崩溃时,系统会在大概一个electionTimeout里不可用;咱们但愿这只占整个时间的很小一部分。
broadcastTime和MTBF都是底层系统的特性,而electionTimeout是咱们必须选择的。Raft的RPC一般要求接收者持久化信息到stable storage,所以broadcastTime的范围在0.5ms到20ms之间,这取决于存储技术。所以,electionTimeout能够取10ms到500ms。一般,server的MTBF是几个月或者更多,所以很容易知足timing requirement。
Raft 和 Paxos 最大的不一样之处就在于 Raft 的强领导特性:Raft 使用领导人选举做为一致性协议里必不可少的部分,而且将尽量多的功能集中到了领导人身上。这样就可使得算法更加容易理解。目前Raft已应用到了 腾讯云消息队列(Cloud Message Queue,CMQ)上。
============参考===========
论文:In Search of an Understandable Consensus Algorithm (Extended Version)
博客:
https://blog.csdn.net/qq_36561697/article/details/88550398
https://www.cnblogs.com/YaoDD/p/6172011.html