Raft协议介绍(翻译)

Raft协议介绍(翻译)

原文链接

原文作者:Shubheksha
翻译:@vision9527
时间:2019年11月

本文讨论的的raft一致性算法来自于论文In Search of An Understandable Consensus Algorithm.所有的引用都出自这篇文章.

Raft

Raft 是一个分布式一致性算法,它的设计很容易被理解。Raft算法解决了即使在出现故障时,多个服务器同意同一个共享状态的问题。共享状态通常是由复制日志数据结构来保证的。只要大多数服务器是有效的,系统就是有效和可运行的。

Raft通过在集群中选举leader来工作和运行。leader负责接受客户端的请求和管理发往集群其他服务器的复制日志。数据流只有一个方向:从leader到其它服务器。

Raft分解一致性问题为三个子问题:

  • leader的选举:leader需要被选举以防存在的leader失效
  • 日志的复制:leader需要保证所有集群所有服务器的日志与它自己的日志同步
  • 安全性:如果服务器已经在某个索引提交了日志条目,那么没有任何其它服务器的日志条目可以在此索引修改

Raft一直保持这些状态都是存在
以上是raft五个比较重要的概念

  • 选举安全:在一个任期内只有一个leader
  • leader只能追加:leader不能覆盖或者删除日志,仅仅能追加日志
  • 日志匹配:如果包含同一个entry的两个日志有相同索引和任期,那么它们之前的的日志都是相同的(译者注:此条件是保持日志一致的最根本原因)。
  • leader完备性:如果一个log在某个任期内提交了,那么这个log会出现在所有更高任期的leader的log中。
  • 状态机安全:如果一个服务器已经在给定的索引值位置的日志条目应用到状态机中,那么其他任何的服务器在这个索引位置不会提交一个不同的日志。

Raft 基础

每个集群中的节点都在此三种状态之一:leader,follower,candidate。
节点状态的改变如下:
节点状态的改变
在通常的状态下,集群只有一个节点是leader,其余节点都是follower。follower是被动的:它们不会主动发起请求,但是会回应来自leader和candidate节点的请求。leader会处理所有来自客户端的请求(如果一个请求发送到了follower,那么那个follower会重定向到leader)。第三种状态,candidate,只是用于选举一个新的leader。

Raft将时间划分为任意长度的任期(term),每个长度以选举开始。如果某个candidate赢得了选举,那么它会是此term的leader直到此term结束。如果投票出现分歧,那么此term会以无leader结束。

任期的期数单调增加。每个节点都会存储当前的任期期数并且在每次交流中都会交换。

如果某个节点的当前任期小于其它节点,那么它会更新它的任期到最大值。如果某个candidate或者leader发现它自己的任期过期了,它们会立刻还原成follower状态。如果某个节点收到错误的任期数,它会拒绝此次请求。

Raft使用两个rpc来执行基本操作:

  • RequestVotes(投票请求),在选举期间由candidate发送
  • AppendEntries(增加条目),由leader用于复制日志和心跳检测(一个检查节点是否存活的信号,不会有任何的日志条目)

Leader 选举

leader周期的发送心跳包给follower维持它的权威(译者注:就是让别的节点认可它是leader的意思)。leader选举会在follower等候来自leader的心跳包超时过后被处罚。此时follower会变成candidate状态并且增加它的任期期数。在它收到投票后,它也会与其它节点并行的发出投票。因此会有三种结果产生:

  • candidate收到来自集群中大多数的投票然后成为leader。接着它发送心跳包给其余节点建立权威。
  • 如果其它的candidate收到AppendEntries RPC,它们会检查任期数。如果任期数大于它们自己当前的任期数,那么它们会接受并承认此leader的状态同时退回成follower的状态。如果任期数小于它们自己当前的任期数,那么它们会拒绝此rpc的请求并且维持candidate的状态。
  • candidate要么退回follower要么成为leader。如果在同一时间有多个节点成为了candidate,投票可能会由于没有明显的多数情况下出现分歧。在这种情况下新的选举会在某个candidate过期后重新进行。

Raft使用随机的选举超时时间(election timeout)来确保投票分歧是少数的,并且分歧的情况也是可以很快被解决的。为了预防第一次投票(译者注:服务启动)出现分歧,election timeout会从固定间隔时间(例如150-300ms)中随机选择。因此在大多数情况只有一个节点会election timeout(目的就是尽量在一个时刻只有一个节点会变成candidate)。这样此candidate节点就会在其它节点election timeout之前赢得选举并发送心跳包给其它节点。同样的机制也用于处理投票分歧。每个candidate会在选举开始时重新获得随机的election timeout,并且在开始下一轮的选举之前等待此超时时间。这样就在新的选举时减少了另一种投票分歧的可能行。

日志复制

客户机请求现在被假定为只写。每个请求都包含被所有节点的复制状态机执行的命令。当leader接受到来自客户端的请求,它将会增加请求到log作为新的entry。每个包含在log中的entry有以下信息:

  • 客户端指定的命令
  • 定位entry在日志中位置的索引(索引从1开始)
  • entry被覆写时识别的任期数

Raft需要复制所有的entry到所有follower节点来保持日志的一致性。leader并行的发布AppendEntries RPC到其它所有节点。leader会重试此操作直到所有的follower安全的复制了新的entry。

leader创建的entry复制给了大多数服务器时,此次请求被认为是提交了。所有之前的entry,包括早期leader创建的也会被认为是提交了。leader一旦执行了提交,马上会返回结果给客户端(译者注:此时并不一定是所有节点都处理了leader请求)。

leader维护它的日志中最大被提交的索引,并且通过AppendEntries RPC发送给其它follower。一旦那些follower发现entry被提交了,它将会把entry按序应用于状态机。

raft维护以下属性,这些属性构成了匹配属性的log:

  • 如果不同日志中的两个条目具有相同的索引和术语,则它们存储相同的命令
  • 如果不同日志中的两个条目具有相同的索引和术语,则前面所有条目中的日志都相同

发送AppendEntries RPC时,leader包含在new entry之前的term number和index。如果follower在自己的日志中找不到此项的匹配项,则拒绝追加new entry的请求。

这个一致性检查让leader得出结论,每当AppendEntries从一个follower成功返回时,它们都有相同的日志,直到index包含在RPC中。

但是在遇到leader崩溃时,leader和follower的日志可能会不一致。

在raft中,leader通过强制follower复制自己的日志来处理不一致问题。这意味着follower日志中冲突的entry将会被来自leader的日志覆盖。

leader会尝试找到最近的与follower日志匹配的索引,并且将会删除额外的日志和增加新日志(译者注:来自leader同步的日志)。

leader将会维护nextindex,这个索引是下一个发送给其它follower的log entry的索引。当一个leader得到权利后,它会将它最近的index的下一个index初始化nextindex值。

无论何时follower对于AppendRPC返回一个失败,leader会减少nextindex并且发布另一个AppendRPC。最终,nextindex会到达一个日志汇集的一个值(译者注:就是leader最新的index)。AppendRPC最终会成功并且它会删除无关的log(如果有)和从leader的log中新增log(如果有)。因此,AppendRPC确保了leader日志和follower是一致的。

鉴于这个机制,leader不需要采取特殊的操作来让follower保持一致性。leader只需要正常的操作,log会在一致性检测失败失败后自动的汇集(译者注:以上机制就能保证数据自动同步,不需要额外的特殊操作)。leader不会覆盖或删除它自己的日志。

安全性

Raft会确保leader在任期内会提前所有之前任期的log。这是为了确保所有日志一致,并且状态机执行相同的命令集所必需的。

在选举期间,RequestVote RPC会包含candidate的日志信息。如果follower发现它自己的log比candidate的日志要新,那么follower不会投票给这个candidate。

Raft通过比较日志中最后一个log的索引和任期来确定两个log中哪个log更为最新。如果log的最后一个具有不同的term,则具有后面term的log更为最新。如果log以相同的term结束,则以较长的log为准

集群成员

为了确保在配置变更时的安全,不应该出现相同任期选举出了两个leader。不幸的是,任何从旧配置直接切换到新配置都是不安全的。

Raft使用两阶段方法来改变集群成员。首先,先切换到一个称为联合共识的中间配置。然后一旦提交,它立刻切换到新的配置。

联合共识允许各个服务器在不同的时间在不同的配置之间进行转换,而不会影响安全性。此外,联合一致性允许集群在整个配置更改期间继续为客户机请求提供服务。

联合共识连接了如下的新旧配置:

  • log 在两个配置的所有节点之间被复制
  • 任何的节点都可以成为leader
  • 同意要来自新旧配置的多数节点
  • 当领导者收到配置更改消息时,它存储并复制join consensus C<old,new>的log。服务器总是使用其日志中的最新配置来做出决策,即使它没有提交。当双方达成共识时,只有日志中包含C<old,new>的服务器才能成为领导者。

现在,leader可以安全地创建一个描述C的日志条目并将其复制到集群。同样,此配置将在每台服务器上看到时立即生效。当根据C的规则提交新配置时,旧配置不相关,并且可以关闭不在新配置中的服务器。

一个很棒的raft协议可视化演示在这里

更多的材料,如演讲、演示、相关论文和开源实现,可以在这里找到

我只研究了组成Raft的基本算法和它提供的安全保证的细节。这篇论文包含了更多的细节,它是非常容易理解的,因为作者的主要目标是理解。我绝对建议你读它,即使你以前从未读过其他的论文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章