Raft论文解读

Raft论文解读

1 简介

Raft是管理复制日志的共识算法。它的作用相当于Paxos算法,但其结构不同于Paxos。这使得Raft比Paxos更容易理解,也为构建实用系统提供了更好的基础。为了提高可理解性,Raft协议将领导者选举日志复制安全保证等共识的关键部分分离,并加强一致性以减少在协议实现中必须考虑的节点状态。

关于共识算法:共识算法允许一组机器作为一个连贯的集体工作,可以在某些成员的失败中幸存下来。因此,在构建可靠的大规模软件系统中发挥着关键作用。

Raft 在许多方面与现有的共识算法相似,但它有几个新颖的特点:

  • 强 leader:Raft 使用比其他共识算法更强的领导形式,例如日志条目(log entries)仅从 leader 流向其他服务器,这简化了复制日志的管理,使 Raft 更容易理解。
  • Leader 选举:Raft 使用随机计时器(randomized timers)来选举 leaders,这仅为任何共识算法已经需要的心跳增加了少量机制,同时简单快速地解决了冲突。
  • Membershipchanges:Raft 用于更改集群中服务集的机制使用一种新的联合共识方法(joint consensus approach),其中两种不同配置的大多数在转换期间重叠,允许集群在配置更改期间继续正常运行。

2 Replicated state machines

所谓的复制状态机,即一个集群中的节点服务器通过复制集群 leader 中的操作日志,进而计算出完全一致的存储数据副本,使得整个集群内的节点服务器存储的数据达到一致。如图所示,每个服务器节点存储包含一系列命令的日志,节点状态机按顺序执行这些命令。由于状态机是确定性的,每个状态机都计算相同的状态和相同的输出序列。因此,集群leader通过对日志信息的控制保证对整个集群服务器的控制,而这种控制就是通过共识算法实现。

在这里插入图片描述

共识算法通常出现在复制状态机的背景下 ,在这种方法中,一组服务上的状态机计算相同状态的相同副本,即使其中某些服务器掉线集群也可以继续运行。 复制状态机可以用于解决分布式系统中的各种容错问题,在各种单一leader集群的大型系统,如 GFS、HDFS 和 RAMCloud,都使用单独的复制状态机来管理 leader 选举并存储必要的配置信息,复制状态机的例子还包括 Chubby和 ZooKeeper 。

共识算法的工作是保持复制日志的一致性,具体来说,服务器上的共识模块接收来自客户端的命令并将它们添加到其日志中,它与其他服务器上的共识模块进行通信,以确保每个服务器上的日志最终包含相同顺序的相同请求,即使某些服务出现故障。一旦命令被正确复制,每个服务的状态机就会按日志顺序处理它们,并将输出返回给客户端。

实际系统的共识算法通常具有以下特性:

  • 在所有非拜占庭条件(non-Byzantine conditions)下的数据安全性(即永远不会返回错误的结果),这些条件包括网络延迟、分区和数据包丢失、重复和重新排序。
  • **只要大多数(这里的大多数是指超过集群总数一半)服务器都可以运行并且可以相互通信和与客户端通信,该集群就可以完全发挥作用(可用)。**因此一个典型的五台服务器集群可以容忍任意两台服务器的故障。假设服务器因停止而失败,他们稍后可能会从可靠存储的状态中恢复并重新加入集群。
  • 不依赖于时间来确保日志的一致性:在最坏的情况下,错误的时钟和极端的消息延迟会导致可用性问题。
  • 在一般情况下,只要集群的大部分服务器响应了一轮远程过程调用(RPC),命令就可以完成,少数慢服务不必影响整体系统性能。

3 What’s wrong with Paxos?

作为第一个被提出的一致性共识算法,Paxos协议确实解决了分布式系统中遇到的许多重要问题,也成为了,共识算法的一个标杆。本小节主要阐述了Paxos在学习与实践中的不足之处:

  • 首先,由于提出Paxos的相关文章过于抽象晦涩了,使得人们在学习Paxos遇到了许多障碍。单决策(single-decree)paxos协议分为两阶段,但这两阶段不能分开理解,且缺少解释。因此,最基本的单决策(single-decree)paxos难以被真正实现。
  • 其次,由于单决策(single-decree)paxos难以实现,人们希望能找到类似的的多决策(multi-decree)paxos协议进行实现,由于在原文章中绝大部分篇幅都在描述单决策(single-decree)paxos,因此,市面上绝大多数分布式系统实现的所谓paxos协议都只是一个类paxos的算法。

4 Designing for understandability

作者认为设计Raft的重点在于尽可能地简化,但这个思想确实主观性太强了,因此并没有很好展开来说,此外就是尽可能的减少每一个服务节点的可能状态。

5 The Raft consensus algorithm

Raft 通过首先选举一个显著的 leader 来实现共识,然后让 leader 完全负责管理复制日志。 leader 接受来自客户端的日志条目,将它们复制到其他服务器上,并告诉服务器何时将日志条目应用到它们的状态机是安全的。 拥有 leader 简化了复制日志的管理,例如 leader 可以在不咨询其他服务器的情况下决定在日志中放置新条目的位置,并且数据以简单的方式从 leader 流向其他服务器。leader 可能会失败或与其他服务器断开连接,在这种情况下会选出新的 leader。

鉴于 leader 方式,Raft 将共识问题分解为三个相对独立的子问题,这些子问题将在以下小节中讨论:

  • Leader 选举:当现有 leader 失败时,必须选择新的leader(第 5.2 节)。
  • 日志复制:leader 必须接受来自客户端的日志条目并在整个集群中复制它们,迫使其他服务器的日志与自己的一致(第 5.3 节)。
  • 安全性:Raft 的关键安全属性是图 3 中的状态机安全属性:如果任何服务器已将特定日志条目应用到其状态机,则其他服务器不能对相同的日志索引应用不同的命令。 5.4 节描述了 Raft 如何保证这个特性;该解决方案涉及对第 5.2 节中描述的选举机制的额外限制。

5.1 Raft basics

一个 Raft 集群包含多个服务器;5 是一个典型的数字,它允许系统容忍两次故障(多数派需要至少3个服务器保持正常运转)。

  • 在任何给定时间,每个服务器都处于以下三种状态之一:leaderfollowercandidate
  • 在正常操作时间中,只有一个 leader,所有其他服务器都是 follower。
  • follower 是被动的:他们不会自己发出请求,而只是回应 leader 和candidate的请求。leader 处理所有客户端请求(如果客户端联系 follower,follower 会将请求重定向到 leader)。
  • candidate状态用于选举一个新的 leader,图 4 显示了状态及其转换;下面讨论这些转换。
    在这里插入图片描述

Raft 将时间划分为任意长度的任期 (term),如图 5 所示,任期为连续的整数编号,每个任期都以选举开始,其中一个或多个candidate尝试成为 leader,如果一个候选者赢得了选举,那么他将在余下的任期内担任 leader 。在某些情况下,选举会导致分裂投票,在这种情况下,任期将在没有领导者的情况下结束;新的任期(有新的选举)将很快开始,Raft 会确保在给定的任期内最多有一个 leader。

在这里插入图片描述
不同的服务器可能会在不同的时间观察到任期之间转换,在某些情况下,一个服务器可能不会观察到选举甚至整个任期。

  • 任期在 Raft 中充当逻辑时钟 ,每个服务器存储一个当前的任期编号,随着时间的推移单调递增。
  • 每当服务器进行通信时都会交换当前任期;如果一台服务器的当前任期小于另一台服务器的当前期限,则将当前任期更新为较大的值。
  • 如果candidate或 leader 发现其任期已过时(收到了来自更大任期服务器的消息),它会立即恢复到 follower 状态。如果服务器收到带有过期任期号的请求,它会拒绝该请求。

Raft 服务使用远程过程调用 (RPC) 进行通信,共识算法只需要两种类型的 RPC:

  • RequestVote RPCcandidate在选举(elections)期间发起,以获取投票(5.2 section)
  • AppendEntries RPC 由 leader 发起以复制日志条目请求,并提供一种心跳形式(5.3 section)。如果服务没有及时收到响应,它们会重试 RPC,并且并行发出 。

5.2 Leader election

Raft 使用心跳机制来触发 leader 选举,当服务器启动时它们以 follower 状态开始,只要服务器从 leader 或candidate那里接收到有效的 RPC 它就会保持 follower 状态。leader 定期向所有 follower 发送心跳RPC(不携带日志条目的 AppendEntries RPC)以维护自身权限。 如果 follower 在称为选举超时(election timeout)的一段时间内没有收到任何信息,则它假定没有可行的 leader 并开始发起选举。

开始选举时 follower 增加其当前任期并转换到 candidate 状态,然后它为自己投票且并行地向集群中的每个其它服务器发出 RequestVote RPC。候选者会一直处于这种状态,直到发生一下三种情况之一:(a) 它赢得选举(b) 另一个服务成为 leader,或**(c)一段时间内没有胜出者**。下面依次讨论这三种情况。

a.

如果一个 candidate 在同一个任期内从这个集群中的大多数服务获得选票,那么它就赢得了选举。每个服务器将在给定的任期内以先到先得的方式投票给至多一名候选者(注意:第 5.4 节增加了对投票的额外限制)。多数规则确保最多一名 candidate 可以赢得特定任期的选举,一旦一个 candidate 赢得了选举它就会成为 leader,然后它向所有其它服务器发送心跳消息以建立其权限并阻止新的选举。

b.

在等待投票时, candidte 可能会收到来自另一台声称是 leader 服务的 AppendEntries RPC。如果该 leader 的任期(包含在其 RPC 中)大于等于 candidate 当前任期,则 candidate 将 leader 视为合法并返回 follower 状态。否则将拒绝 RPC 并继续处于 candidate 。

c.

第三种可能的结果是这次选举candidate既没赢也没输:如果同时有多个 follower 成为 candidate ,可能会分裂选票,导致没有 candidate 获得多数票,发生这种情况时,每个 candidate 将超时并通过增加其任期并启动另一轮 RequestVote RPC 来开始新的选举。然而如果没有额外的措施,分裂选票可能会无限期地重复。

Raft 集群每个服务器使用随机选举超时时间来确保分裂选票很少发生并能被快速解决,为了防止分裂投票,选举超时时间是从固定间隔(例如 150-300ms)中随机选择的。以便在大多数情况下只有一个服务器会超时,8.3 节表明这种方法可以快速选举 leader。

5.3 Log replication

一旦选举出 leader ,就会开始为客户端请求提供服务,每个客户端请求都包含一个要由复制状态机执行的命令,leader 将命令作为新条目(entry)附加到其日志中,然后并行地向其它每个服务器发出 AppendEntries RPC 以复制条目。当条目被安全复制后(如下所述),leader 将条目应用于其状态机并将该执行的结果返回给客户端。如果 follower 崩溃或运行缓慢,或者网络数据包丢失,leader 会无限期地重试 AppendEntries RPC(即使它已经响应了客户端),直到所有 follower 最终存储所有日志条目。

日志的组织方式如图 6 所示,每个日志条目存储一个状态机命令以及 leader 收到条目时的任期号(term)。日志条目中的任期编号用于检测日志之间的不一致并确保图 3 中的某些属性,每个日志条目还有一个整数索引,用于标识其在日志中的位置。

在这里插入图片描述

leader 决定何时将日志条目应用到状态机是安全的;这样的条目称为已提交。Raft 保证提交的条目是持久的,并且最终会被所有可用的状态机执行,注意这里的描述词是最终,一旦创建条目的 leader 在大多数服务器上复制了它(例如,图 6 中的条目 7)就会提交一个日志条目。 这也会提交 leader 日志中的所有先前条目,包括由以前的 leader 创建的条目,第 5.4 节讨论了在 leader 变更后应用此规则时的一些微妙之处,并且还表明这种承诺的定义是安全的。leader 跟踪它知道要提交的最大索引,并将该索引包含在未来的 AppendEntries RPC(包括心跳)中,以便其他服务最终发现。一旦 follower 得知一个日志条目被提交,它就会将该条目应用到它的本地状态机(按日志顺序)。

Raft 维护了以下属性,它们共同构成了图 3 中的日志匹配属性:

  • 如果不同服务器日志中的两个条目具有相同的索引和任期,则它们存储相同的命令。
  • 如果不同服务器日志中的两个条目具有相同的索引和任期,则该日志在所有前面的条目中都是相同的。

第一个属性,即 leader 在给定任期内创建的每一个日志条目都是独一无二的,并且日志条目永远不会改变它们在日志中的位置。 第二个属性由 AppendEntries 执行的简单一致性检查保证。在发送 AppendEntries RPC 时,leader 在其日志中一定包含紧接在新条目之前的特定任期和索引号的条目,因此如果 follower 在其日志中没有找到具有相同索引和任期的条目,则它拒绝新条目,这也是可以解释为什么 follower 最终都能保证所含所有日志的顺序性完整性

一致性检查可归纳为步骤:日志的初始空状态满足日志匹配属性(Matching Property),并且一致性检查在日志扩展时保留日志匹配属性。 满足以上两个属性后,每当 AppendEntries 成功返回时,leader 就知道 follower 的日志通过新条目与自己的日志相同。

正常运行时 leader 和 follower 的日志保持一致,因此 AppendEntries 一致性检查永远不会失败,但是 leader 崩溃会使日志不一致(旧的 leader 可能没有完全复制其日志中的所有条目),这些不一致可能会导致一系列 leader 和 follower 崩溃,图 7 说明了follower 的日志可能与新 leader 的日志不同的方式,follower 可能缺少 leader 上存在的条目,它可能有 leader 上不存在的额外条目,或两者兼而有之。日志中缺失和无关的条目可能跨越多个任期。

在这里插入图片描述

在 Raft 中 leader 通过强制 follower 的日志复制自己 (leader) 的日志来处理不一致,这意味着 follower 日志中的冲突条目将被 leader 日志中的条目覆盖,第 5.4 节将表明,当再加上一个限制时,这是安全的。

为了使 follower 的日志与自己的一致,leader 必须找到两个日志一致的最新日志条目,删除该点之后 follower 日志中的任何条目,并将该点之后 leader 的所有条目发送给 follower,所有这些操作都是为了响应 AppendEntries RPC 执行的一致性检查而发生的。leader 为每个 follower 维护一个 nextIndex,这是 leader 将发送给该 follower 的下一个日志条目的索引,当 leader 第一次上任时,它将所有 nextIndex 值初始化为其日志中最后一个之后的索引(图 7 中的 11)。如果一个 follower 的 log 与 leader 的不一致,则 AppendEntries 一致性检查将在下一次 AppendEntries RPC 中失败,拒绝后 leader 递减 nextIndex 并重试 AppendEntries RPC,最终 nextIndex 将达到 leader 和 follower 日志匹配的点。发生这种情况时,AppendEntries 将成功,它会删除跟随者日志中的任何冲突条目并附加 leader 日志中的条目(如果有)。一旦 AppendEntries 成功,follower 的日志与 leader 的日志一致,并且在剩余的任期内将保持这种状态。

该协议可以被优化以减少被拒绝的 AppendEntries RPC 的数。

有了这种机制,leader 在上任时不需要采取任何特殊的行动来恢复日志的一致性。它刚刚开始正常运行,follower 的日志会自动收敛以响应 AppendEntries 一致性检查失败,leader 永远不会覆盖或删除自己日志中的条目(图 3 中的 leader 仅附加属性)。

这种日志复制机制展示了第 2 节中描述的理想共识属性:只要大多数服务器都启动,Raft 就可以接受、复制和应用新的日志条目; 在正常情况下,可以通过一轮 RPC 将新条目复制到集群的大多数; 并且单个慢 follower 不会影响性能。

5.4 Safety

前面的部分描述了 Raft 如何选举 leader 和复制日志条目,然而到目前为止描述的机制还不足以确保每个状态机以相同的顺序执行完全相同的命令,例如当 leader 提交多个日志条目时 follower 可能不可用,然后它可以被选为 leader 并用新的条目覆盖这些条目; 因此,不同的状态机可能会执行不同的命令序列。

本节通过添加对哪些服务器可以被选为 leader 的限制来完成 Raft 算法,该限制确保任何给定任期的 leader 都包含之前任期中提交的所有条目(图 3 中的 leader 完整性属性)。

5.4.1 Election restriction

在任何基于 leader 的共识算法中,leader 最终必须存储所有提交的日志条目。在某些共识算法中,例如 Viewstamped Replication ,即使 leader 最初不包含所有已提交的条目,也可以选举出领导者。这些算法包含额外的机制来识别丢失的条目并将它们传输给新的 leader,无论是在选举过程中还是之后不久。不幸的是,这会导致相当多的额外机制和复杂性,Raft 使用了一种更简单的方法,它保证从选举的那一刻起,每个新 leader 都存在以前任期的所有提交条目,而无需将这些条目传输给 leader,这意味着日志条目只向一个方向流动,从 leader 到 follower,leader 永远不会覆盖他们日志中的现有条目。

Raft 使用投票过程来防止 candidate 胜出选举,除非其日志包含所有已提交的条目。 candidate 必须与集群的大多数成员联系才能当选,这意味每个提交的条目必须至少存在于其中一个服务中。如果候选者的日志至少与该(集群)多数中的任何其它日志一样最新(“最新”定义如下)那么它将保存所有提交的条目。 RequestVote RPC 实现了这个限制:RPC 包含有关 candidate 日志的信息,如果选民自己的日志比候选人的日志更新,则选民拒绝投票。

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

5.4.2 Committing entries from previous terms

如第 5.3 节所述,一旦该条目存储在大多数服务器上 leader 就知道其当前任期中的条目已提交,如果 leader 在提交条目之前崩溃,未来的 leader 将尝试完成复制该条目。然而 leader 不能立即断定前一任期的条目存储在大多数服务器上就代表着已提交。 图 8 说明了一种情况,旧日志条目存储在大多数服务器上,但仍然可能被未来的 leader 覆盖。
在这里插入图片描述

为了消除图 8 中的问题,Raft 从不通过计算副本数来提交之前任期的日志条目,通过计算副本数仅提交来自 leader 当前任期的日志条目; 一旦以这种方式提交了当前任期中的条目,那么由于日志匹配属性,所有先前的条目都将被间接提交。 在某些情况下,leader 可以安全地得出一个较旧的日志条目已提交的结论(例如,如果该条目存储在每个服务器上),但 Raft 为简单起见采取了更保守的方法。

Raft 在提交规则中会产生这种额外的复杂性,因为当 leader 从以前的任期复制条目时,日志条目会保留其原始任期号。在其他共识算法中,如果一个新的 leader 从之前的“任期”中重新复制条目,它必须使用新的“任期号”这样做。Raft 的方法使推理日志条目变得更容易,因为它们随着时间的推移和跨日志保持相同的术语编号。此外与其他算法相比,Raft 中的新 leader 发送的先前条款中的日志条目更少(其他算法必须发送冗余日志条目以重新编号,然后才能提交)。

5.4.3 Safety argument

通过给定完整的 Raft 算法,我们现在可以更准确地论证 Leader 完整性属性(Leader Completeness Property )成立(这个论证基于安全性证明;参见第 8.2 节)。我们假设 Leader 完整性属性不成立,那么我们证明一个矛盾,假设任期 T (leaderT) 的 leader 提交了其任期内的日志条目,但该日志条目并未由未来某个任期的 leader 存储,考虑最小的项 U > T,其 leader (leaderU) 不存储条目。

  1. 在 leaderU 选举时,提交的条目必须不存在于 leaderU 的日志中(leader永远不会删除或覆盖条目)。
  2. leaderT 在集群的大多数成员上复制了条目,leaderU 收到了集群大多数成员的投票。因此至少有一个投票者同时接受了来自 leaderT 的条目并投票给了 leaderU,如图 9 所示,投票者是达成矛盾的关键。
  3. 在投票给 leaderU 之前,投票者必须已经接受了 leaderT 提交的条目;否则它会拒绝来自 leaderT 的 AppendEntries 请求(其当前任期将高于 T)。
  4. 投票者在投票给 leaderU 时仍然存储该条目,因为每个介入的领导者都包含该条目(假设),领导者从不删除条目,而追随者仅在与领导者冲突时删除条目。
  5. 投票者投票给了 leaderU ,所以 leaderU 的日志必须和选民的一样最新,这导致了两个矛盾之一。
  6. 首先,如果投票者和 leaderU 共享相同的最后一个日志任期,那么 leaderU 的日志必须至少和投票者一样长,所以 leaderU 的日志包含了投票者日志中的每一个条目。这是一个矛盾,因为投票者包含提交的条目,而 leaderU 被认为没有。
  7. 否则,leaderU 的最后一个日志任期必须大于投票者的。此外它大于 T,因为投票者的最后一个日志期限至少是 T(它包含来自期限 T 的提交条目)。创建 leaderU 的最后一个日志条目的之前的 leader 必须在其日志中包含已提交的条目(假设)。那么根据日志匹配属性,leaderU 的日志也必须包含提交的条目,这是一个矛盾。
  8. 这就完成了矛盾。因此,所有任期大于 T 的 leader 必须包含任期 T 中提交的所有条目。
  9. 日志匹配属性( Log Matching Property)保证未来的 leader 也将包含间接提交的条目,例如图 8(d) 中的索引 2。

以上的证明部分略繁琐,总结来说,就是任何一个leader的产生都代表这个leader包含先前leader的所有已提交日志

鉴于领导者完整性属性,我们可以从图3中证明状态机安全属性(State Machine Safety Property ),该属性指出,如果服务器已将给定索引处的日志条目应用于其状态机,则不会有其他服务器应用另一个相同索引的日志条目。当服务器将日志条目应用于其状态机时,其日志必须与通过该条目的领导者的日志记录相同,并且必须提交该条目。现在考虑任何服务器应用给定日志索引的最低期限;日志完整性属性保证所有较高任期的领导者将存储相同的日志条目,因此以较晚任期应用索引的服务器将应用相同的值。因此,状态机安全性能成立。

最后,Raft要求服务器以日志索引顺序应用条目。结合状态机安全属性,这意味着所有服务器将以相同的顺序将完全相同的日志条目集应用于其状态机。

5.5 Follower and candidate crashes

Raft中的 follower 和 candidate 崩溃比 leader 崩溃更容易处理,并且它们都以相同的方式处理。 如果 follower 或candidate 崩溃,那么未来发送给它的 RequestVote 和 AppendEntries RPC 将失败。Raft 通过无限重试来处理这些失败;

  • 如果崩溃的服务重新启动则 RPC 将成功完成。
  • 如果服务器在完成 RPC 之后但在响应之前崩溃,那么它会在重新启动后再次收到相同的 RPC。

Raft RPC 是幂等的,所以这不会造成损害,例如如果一个 follower 收到一个 AppendEntries 请求,其中包括其日志中已经存在的日志条目,follower 会忽略新请求中的这些条目。

关于程序函数中的幂等性,并不是保证每次调用函数都得到相同的内容,而是保证外部接口的调用不会改变函数内部的情况,假如出现了以外的错误而失败,也可以在重试下还原理想的情况

5.6 Timing and availability

对 Raft 的要求之一是安全性不能依赖于时间:系统不能仅仅因为某些事件发生得比预期的快或慢而产生错误的结果。然而,可用性(系统及时响应客户的能力)必须不可避免地取决于时间。 例如,如果消息交换比服务器崩溃之间的典型时间要长,candidate 就不会坚持到赢得选举;没有稳定的 leader ,Raft 无法取得进展。

Leader 选举是 Raft 最关键的方面,只要系统满足以下时序要求,Raft 将能够选举和维护一个稳定的 leader:
b r o a d c a s t T i m e ≪ e l e c t i o n T i m e o u t ≪ M T B F broadcastTime ≪ electionTimeout ≪ MTBF broadcastTimeelectionTimeoutMTBF
在这个不等式中 broadcastTime 是服务向集群中的每个服务并行发送 RPC 并接收它们的响应所花费的平均时间;electionTimeout 是第 5.2 节中描述的选举超时时间;MTBF 是单个服务器的平均故障间隔时间。广播时间应该比选举超时时间少一个数量级,以便 leader 可以可靠地发送阻止 followers 开始选举所需的心跳消息; 鉴于用于选举超时的随机方法,这种不平等也使得分裂选票不太可能。选举超时应该比 MTBF 小几个数量级,以便系统稳步前进。当 leader 崩溃时系统将在大约选举超时时间内不可用;我们希望这仅占总时间的一小部分。

BroadcastTime 和 MTBF 是底层系统的属性,而 electionTimeout 是我们必须选择的。Raft 的 RPC 通常需要接收方将信息持久化到稳定的存储中,因此 broadcastTime 可能在 0.5 毫秒到 20 毫秒之间,具体取决于存储技术。 因此 electionTimeout 很可能在 10 毫秒到 500 毫秒之间,典型的服务 MTBF 为几个月或更长时间,很容易满足时序要求。

6 Cluster membership changes

关于集群中的配置更改,为了使配置更改机制安全,在过渡期间不能有可能在同一任期内选举两个 leader 的情况。不幸的是,服务直接从旧配置切换到新配置的任何方法都是不安全的。一次原子地切换所有服务是不可能的,因此在转换过程中,集群可能会分裂为两个独立的大多数(参见图 10)。
在这里插入图片描述

为了确保安全,配置更改必须使用两阶段方法,有多种方法可以实现这两个阶段,例如一些系统使用第一阶段禁用旧配置,使其无法处理客户端请求;然后第二阶段启用新配置。在 Raft 中,集群首先切换到我们称之为联合共识( joint consensus)的过渡配置;一旦达成联合共识,系统就会转换到新的配置。联合共识结合了新旧配置:

  • 日志条目被复制到两种配置中的所有服务器。
  • 任一配置中的任何服务器都可以充当 leader。
  • 达成协议(用于选举和日志条目提交)需要与新配置和旧配置不同的多数派。(目标是使联合共识的服务器达到大多数

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

集群配置使用特殊日志条目进行存储和通信。图 11 说明了配置更改过程,当 leader 收到将配置从 C o l d C_{old} Cold更改为 C n e w C_{new} Cnew 的请求时,它将联合共识的配置(图中的 C o l d , n e w C_{old,new} Cold,new)存储为日志条目,并使用前面描述的机制来复制该条目。 一旦给定的服务器将新的配置条目添加到其日志中,它将使用该配置进行所有未来的决策(服务器始终使用其日志中的最新配置,无论该条目是否已提交),这意味着 leader 将使用 C o l d , n e w C_{old,new} Cold,new 的规则来确定 C o l d , n e w C_{old,new} Cold,new 日志条目何时被提交。如果 leader 崩溃,则可能会在 C o l d C_{old} Cold C o l d , n e w C_{old,new} Cold,new 下选择新的 leader,这取决于获胜 candidate 是否收到了 C o l d , n e w C_{old,new} Cold,new 。在任何情况下 C n e w C_{new} Cnew 都不能在此期间做出单方面的决定。
在这里插入图片描述

一旦 C o l d , n e w C_{old,new} Cold,new 被提交,当前时期只有 C o l d , n e w C_{old,new} Cold,new服务器才能控制整个集群,并且 Leader 完整性属性确保只有具有 C o l d , n e w C_{old,new} Cold,new 日志条目的服务器才能被选举为 leader,现在 leader 可以安全地创建一个描述 C n e w C_{new} Cnew 的日志条目并将其复制到集群中。 同样,此配置将在每个服务器上存在后立即生效,当新的配置在 C n e w C_{new} Cnew 的规则下被提交后,旧的配置是无关紧要的,不在新配置中的服务器可以被关闭。 如图 11 所示, C o l d C_{old} Cold C n e w C_{new} Cnew 没有时间可以同时做出单方面决策,这保证了安全。

重新配置还有三个问题需要解决:

  • 第一个问题是新服务器最初可能不会存储任何日志条目,如果在这种状态下将它们添加到集群中,它们可能需要很长时间才能赶上。为了避免可用性的差距,Raft 在配置更改之前引入了一个额外的阶段,在这个阶段,新服务作为非投票成员加入集群(leader 将日志条目复制给他们,但他们不被考虑为多数),一旦新服务赶上集群的其余部分,重新配置就可以如上所述进行。

下面这个太抽象了,没搞明白。

  • 第二个问题是集群 leader 可能不是新配置的一部分。在这种情况下,一旦提交了 C n e w C_{new} Cnew 日志条目 leader 就会下台(返回 follower 状态),这意味着当 leader 会有一段时间管理一个不包括自己的集群时(在它提交 C n e w C_{new} Cnew 时); 它复制日志条目但自身并不计算在其中,在提交 C n e w C_{new} Cnew 时发生 leader 转换,因为这是新配置可以独立运行的第一个点(始终可以从 C n e w C_{new} Cnew 中选择 leader)。在此之前,可能是只有来自 C o l d C_{old} Cold的服务可以选择 leader。
  • 第三个问题是移除的服务器(那些不在 C n e w C_{new} Cnew 中的)可能会破坏集群,这些服务器不会收到心跳,因此它们将超时并开始新的选举。然后他们将发送带有新任期号的 RequestVote RPC,这将导致当前 leader 恢复到 follower 状态。最终会选出一个新的 leader,但是被移除的服务会再次超时,这个过程会重复导致可用性较差。

为防止出现此问题,服务器在认为当前 leader 存在时会忽略 RequestVote RPC。 具体来说,如果服务器在听取当前 leader 的最小选举超时时间内收到 RequestVote RPC,则不会更新其任期或授予其投票。这不影响正常选举,其中每个服务器至少等待最小选举超时开始选举。这也有助于避免被移除的服务器造成的中断:如果 leader 能够获得其集群的心跳,那么它就不会被更大的任期号废黜。

7 Log compaction

Raft的日志在正常操作期间不断增长以合并更多客户端请求,但在实际系统中,日志不能在没有约束的情况下增长。随着日志变长,它占用更多空间并需要更多时间重放(replay)。如果没有某种机制来丢弃日志中累积的过时信息,这最终将导致可用性问题。

快照是最简单的压缩方式,并且已经运用在包括Chubby,Zookeeper在内的多种分布式系统中。

压缩的增量方法,如日志清理和日志结构化合并树,都是可用的。这些操作每次仅对一小部分数据进行,随着时间的推移会均匀地压缩日志数据。这些方法首先会选择删除和重写操作较多的日志部分,然后对整体日志进行压缩整理,最终释放空间。这些方法相较快照来说更加复杂需要设计额外的机制,因为你需要对全局的日志情况进行分析并在压缩部分日志的情况下保持整体的可用性。

Raft中的快照压缩如图12所示,Raft中的每个服务器都独立地对本地已提交的日志进行压缩,并覆盖原先日志的位置。在快照压缩时,Raft服务器除了压缩日志,还需要记录当前状态机的状态以及一些重要的元数据信息:例如快照压缩的最后一条日志条目的索引以及所处的任期等等。这样做的原因是为了保证AppendEntries API的一致性检查。此外,快照还需要特别的保留之前的配置信息,以便集群整体在配置变更时之需。
在这里插入图片描述

尽管大多数服务器的快照都是自身生成,对于那些日志条目落后较多的服务器, leader 也会直接令其复制自身的快照。 leader 通过InstallSnapshot向落后的服务器传递快照。尽管如此,follower仍需要在快照和自身已有的日志条目中做出选择。通常情况下,follower都会丢弃自身的日志条目转而跟 leader 保持一致。

这种快照压缩方法偏离了Raft的强 leader 原则,因为follower可以在不了解 leader 的情况下进行快照压缩。但是,这种偏离也是合理的。虽然有 leader 有助于避免冲突的决策达成共识,但在进行快照压缩时已经达成共识,因此没有决策冲突。

另外一种可能的尝试是仅允许 leader 制作快照,并定期发送到其他的follower。但这样的做法不仅浪费了网络带宽,也使得发送机制变得复杂。此外,服务器在什么时候采用快照压缩也决定了快照压缩的性能表现,太过于频繁的压缩会影响服务器的整体性能,如何设定压缩的边界也需要经过仔细地设计。快照压缩地落盘通常使用写时复制,以便不影响其他常规操作。

8 Client interaction

客户端第一次对Raft地连接请求必须连接到 leader ,如果连接到了其他的服务器,请求会被拒绝,除非 leader 宕机,请求才会被其他服务器接收。Raft协议的最终目的是达成一个集群的线性化语义(一致性语义),因此在执行任何命令时都需要检查日志条目的序号,这也是为了保证上面提到过的幂等性。

只读操作通常不涉及对日志条目的写处理,因此有可能返回旧数据。为了保证不出现这样的问题,leader通常采用两个措施解决:1. leader 必须时刻清楚当前已提交的最终日志条目的位置,由于leader可能是刚刚上任的,因此,leader会在任期开始的时候提交一条 no-up 空日志条目,以保证之前的日志条目全部被提交。2.以及,在对任何只读请求进行恢复之前,查看自己当前是否正常工作而没有脱离集群,即进行一次紧急的心跳信息交换。


以上就是关于Raft论文(In Search of an Understandable Consensus Algorithm)的绝大部分内容的翻译解释。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值