翻译 | 理解Raft共识算法:对一份学术论文的总结
这篇文章总结了由Diego Ongaro和John Ousterhout所发表的论文《In Search of An Understandable Consensus Algorithm》里所描述的Raft共识算法。所有的引用都来自该论文。
Raft:
Raft是一种分布式共识算法。它被设计的易于理解。用来解决多台服务器在共享状态上即使发生故障也能达成一致的问题。共享状态通常是一个通过复制日志来支持的数据结构。我们需要确保只要大多数服务在运行时,整个系统就可以全面运转。
Raft通过在集群里选择出一个领导者的方式来工作。这个领导者负责接收客户端请求以及管理对其他服务器的日志复制。数据的流向只有一个方向:从领导者流向其他服务器。
Raft将共识拆解成以下三个子问题:
领导者选举(Leader Election):在现有的领导者出现故障的情况下,需要选举出一个新的领导者。
日志复制(Log replication):领导者需要通过复制来确保所有服务器上的日志与自身的保持同步。
安全性(Safety):如果服务器中的一台已经在特定的索引提交了一份日志条目,那么没有任何其他服务器可以向这个索引再提交一份不同的日志条目。
Raft在任何时候都确保以下这些特性为真:
选举的安全性(Election Safety):在一个指定的任期内最多只会有一个领导者被选举出来。
领导者只追加(Leader Append-Only):领导者永远都不会覆盖或者删除它自己的日志条目;它只会追加新增的条目。
日志的匹配处理(Log Matching):如果两份日志里含有一个具有相同索引和任期编号的条目,那么在这个索引之前的所有日志条目都是相同的。
领导者的完整性(Leader Completeness):如果一份日志条目在一个特定的任期内被提交了,那么这份条目也将会出现在所有后续更高任期编号的领导者的日志中。
状态机的安全性(State Machine Safety):如果一台服务器已经将一份指定索引的日志条目应用到它的状态机里了,那么没有任何其他服务器会将别的的日志条目应用到该索引。
基本原理:
每台服务器都处于这三种状态之一:领导者(Leader)、追随者(Follower)、候选者(Candidate)。
在正常的操作下,只会有一个领导者,而其他的所有服务器都是追随者。追随者是被动的:它们不处理任何属于自己的请求,仅仅只响应来自领导者和候选者的请求。领导者处理所有的客户端请求(如果客户端联系了一个追随者,追随者会将它重定向给领导者)。第三种状态,即候选者,是用于新领导者的选举。
Raft将时间分割成一个个随机长度的任期,每一个都从一次选举开始。如果一个候选者赢得了选举,它将在任期余下的时间里保持领导者身份。如果投票发生了分歧,那么这次任期就会在没有领导者的情况下终止。
任期编号只会单调递增。每台服务器都存储着当前任期编号,在每一次通讯里也都会进行交换。
…如果一台服务器的当前任期记录小于其他服务器的,那么它就会将自己的当前任期记录更新到更大的那个值。如果一个候选者或领导者发现自己的任期记录已经过时了,它立即就会转换成追随者状态。如果一台服务器收到一个带有旧任期编号的请求,就会拒绝该请求。
Raft利用两个远程过程调用(RPCs)来实施它的基本操作。
请求投票(RequestVotes)用于选举期间的候选者
追加条目(AppendEntries)被领导者用来复制日志,此外作为心跳检测(一种用来检测一台服务器是否正常运行的信号——不含有任何日志条目内容)
领导者选举
领导者周期性地向它的追随者们发送心跳检测,以此来维持授权。当一个追随者在等待领导者的心跳检测时超时了,就会触发一次领导者选举。这个追随者会转变成候选者状态,并增加它的任期编号。在投票给自己之后,就并行地向集群里的其他服务器发起请求投票的RPC。有三种可能的结果:
候选者获得了大多数服务器的投票并成为领导者。然后向集群里的其他服务器发送一次心跳从而完成授权。
如果其他的候选者收到了追加条目的RPC,它们会检测其中的任期编号。如果这个任期编号大于自己的记录,就会接受来源服务器作为领导者,且转回追随者状态。如果其中的任期编号较小,就拒绝该RPC并继续保持为候选者。
候选者既没有输也没有赢。如果超出一台服务器同时变成候选者,投票就会因没有明确的多数票而分歧。这种情况下就会在某一个候选者超时后开始新的一次选举。
Raft采用随机的选举超时时间来确保投票分歧极少发生,且也会被快速解决。首选,为了防止投票分歧,选举的超时时间会从一个固定的间隔里随机选择(例如, 150-300毫秒)。这会把服务器分散开来,从而在大多数情况下将只会有一台服务器发生超时;且在其他任何服务器超时之前赢得选举并发送心跳检测。用于处理投票分歧的机制也一样。每个候选者在一次选举的开始时重置其随机的选举超时时间,并等待着超时时间的流逝,直到开始下一次选举;这减少了在新一次选举里再次发生选举分歧的可能性。
日志复制:
目前为止客户端的请求被假定为只写。每一次请求包含一个理想情况下会被所有服务器上冗余的状态机所执行的命令。当领导者获得客户端请求时,它将其作为新条目添加到自己的日志里。在日志里的每个条目:
包含客户端具体指定的命令
有一个索引用来指明条目在日志数据里所处的位置(索引从1开始)
有一个任期编号用来在逻辑上指明条目是什么时候写入的
它需要将条目复制到所有的追随者节点,从而保持日志的一致性。领导者并行地向所有其他服务器发起追加条目的RPC。领导者会进行重试直至所有追随者安全地复制到这个新条目。
当条目被创建它的领导者复制到大部分服务器上后,就认为是完成提交了。所有之前的条目,包括那些由更早领导者创建的,也都认为是完成提交了。一旦完成提交,领导者就处理了该条目并返回结果给客户端。
领导者维护着它所知的已被提交到自己日志里的最高索引,并随着追加条目的RPC发送给它的追随者们。一旦追随者发现条目已完成提交,它就按顺序将条目应用到它的状态机里。
Raft维护着以下进行日志匹配的特性:
• 如果在不同日志里的两个条目具有相同的索引和任期编号,那么它们存储着相同的命令。
• 如果在不同日志里的两个条目具有相同的索引和任期编号,那么这些日志之前的所有条目也都相同。
当发送一次追加条目的RPC时,领导者会把在新条目之前一个条目的任期编号和索引包括进去。如果追随者不能在自己的日志里找到匹配的条目,就会拒绝该追加新条目的请求。
这个一致性检测让领导者可以推断出无论何时追随者追加条目返回成功,那么它们到该RPC中所包含的索引为止具有相同的日志。
但是当领导者崩溃时,领导者和追随者的日志可能变得不一致。
在Raft里,领导者通过强制追随者拷贝自己日志的方式来处理这些矛盾。这意味着在追随者日志里冲突的条目将会被来自领导者日志的条目所覆盖。
领导者试图找出自己与追随者日志中相匹配的最近索引,删除存在的多余条目,然后添加新的条目。
领导者维护着每个追随者的下一个索引,也就是领导者将会发送给追随者的下一份日志条目的索引。当一个领导者首次掌管时,会将所有的下一个索引初始化到自身日志最近的条目之后。
任何时候,追加条目RPC里返回一个追随者失败的结果时,领导者就会递减其下一个索引,并另起一个追加条目的RPC。最终,下一个索引将会触达一个双方日志相交的值。当这样时,追加条目将会成功,且可以移除无关的条目(如果有的话)并增加来自领导者日志的新条目(如果有的话)。因此,追随者一次成功的追加条目确保了领导者的日志与其是一致的。
基于这个机制,当领导者开始掌管时,无需采取任何特殊的操作来恢复日志一致性。只需要按正常操作开始即可,并且作为追加条目一致性检测失败的响应处理,日志会自动地归于一致。
安全性:
Raft保证了一个任期内的领导者在它的日志里拥有所有之前任期已经完成提交的条目。对确保所有日志是一致的以及状态机都执行相同命令集合来说,这是必须的。
在一次领导者选举期间,请求投票的RPC包含关于候选者日志的信息。如果投票者发现它的日子比候选者更新,则不会为其投票。
Raft通过对比日志里最近条目的索引和任期编号来判断两份日志之间谁更新。如果日志最近条目的任期编号不同,则拥有较近任期编号的日志是更新的。如果日志止于相同的任期编号,那么哪个日志更长哪个就是更新的。
集群成员关系:
为了配置变更逻辑是安全的,在过渡期间必须不会出现一个任期里选举出两个领导者的可能时刻。不幸的是,服务器直接从旧配置切换到新配置的任何方式都是不安全的。
Raft采用一种两阶段的方式来更改集群成员关系。首先,它会切换到一个中间的配置,称为多边共识(joint consensus)。然后,一旦完成提交,就切换新的配置。
多边共识允许各服务器在不同的时间里进行配置过渡,而又不破坏安全性。而且,多边共识允许集群在变更配置期间继续受理客户端的请求。
多边共识按照如下的方式结合新老配置:
日志条目被复制到两种配置下的所有服务器上
来自新老配置的任何服务器都可以成为领导者
在新老配置下都分别要达到大多数才可以达成一致
当领导者收到一条配置变更消息时,它会存储并复制用于加入共识C<old,new>的条目。一台服务器总是使用在它日志上最新的配置去做决策,即使是还没完成提交的。当多边共识完成提交后,只有在日志中有C<old,new>的服务器可以成为领导者。
现在领导者可以安全地创建一份描述C<new>的日志条目并复制到集群里。再次,这个配置在每台服务器上只要一触达就会生效。当新配置在C<new>的规则下完成提交时,老配置就无用了,且不在新配置里的服务器可以关闭了。
一个非常棒的演示Raft是如何运作的视图见此(http://thesecretlivesofdata.com/raft/)。
诸如对话、介绍、相关论文以及开源的实现可发现于此(https://raft.github.io/)。
我也只仅仅挖掘了一些构成Raft及其安全性保证的基本算法的一些细节。该论文里包含了更多的细节并且超级平易近人,因为作者的主要目标是为了易于理解。我十分的推荐你去阅读它(译者按:就不),即使你之前还从没阅读过其他论文。
“
译后感
文中所述,该算法通过极大概率规避在一个任期内出现两个候选者的可能;并且如果出现了此类情况,则也是直接放弃任期,并等待下一次任期的开始,直到只有一个候选者的情况。其实所谓的随机时长任期和投票分歧直接放弃的处理,只是算法层面对状态判断、性能考虑和概率情况的一个适中选择。这里与其说是选举,不如说是轮值制,而投票是对新轮值服务器上岗前的一个考核,看看其工作(日志信息)是否跟上了大部队的节奏。
”
原文链接:
https://www.freecodecamp.org/news/in-search-of-an-understandable-consensus-algorithm-a-summary-4bc294c97e0d/