Loading

Distributed | Raft

1. 复制状态机

一致性算法是在复制状态机的背景下产生的。在这种方法下,一组服务器的状态机计算相同状态的相同副本,即使某些服务器宕机,也可以继续运行。

复制状态机通常使用复制日志实现,每个服务器存储一个包含一系列命令的日志,每个日志中命令相同并且顺序也相同。因此每个状态机可以处理相同的命令序列,得到相同的状态和输出序列。

一致性算法的工作就是保证复制日志的一致性。每台服务器上的一致性模块接收来自客户端的命令,并将他们添加到日志中。一致性模块互相通信,以确保每个日志最终以相同的顺序包含相同的命令。

2. Paxos的问题

  1. 非常难以理解,描述晦涩难懂,很少有人能够成功地理解
  2. 没有为实现提供良好的基础。缺少对mutil-Paxos的细节描述。因此实际的系统可能和Paxos的区别很大。

3. Raft一致性算法

Raft首先选举一个Distinguished leader,然后由它全权负责管理复制日志来实现一致性。

Leader从客户端接收日志条目,把日志条目复制到其他服务器上,并在保证安全性的时候通知其他服务器执行日志。拥有一个leader极大地简化了对复制日志的管理,但leader可能宕机,也可能和其他服务器断开连接,这时需要选举一个新的Leader。

基于以上设定,Raft将一致性问题分解为三个相对独立的子问题:

  1. Leader选举:当一个Leader停止工作,一个新的Leader必须被选举出来
  2. 日志复制:Leader必须从客户端接收日志条目然后复制到集群的其他节点,并且必须保持一致。
  3. 安全性:如果有任何的服务器节点已经应用了一个特定的日志条目到它的状态机中,那么其他服务器节点不能在同一个日志索引位置应用一条不同的指令。

4. Raft基础

一个Raft集群包含若干个服务器节点,通常为5个,因此可以容忍两个节点的失效。

在任意时刻,每个节点都处于三个状态之一:leader, follower, candidate

在正常情况下,集群中只有一个leader,剩下的服务器均为followerfollower都是被动的,不会发送任何请求,只是简单的响应来自leadercandidate的请求。leader处理所有的客户端请求(给follower的请求会被重定向到leadere

image

如图为节点间状态的相互转换。Raft将时间分割成任意长度的任期,每个任期从一次选举开始,一个或多个candidate尝试成为leader。如果一个candidate赢得选举,它将在该任期剩下的时间充当leader,在某些情况下,一次选举无法选出leader,这一任期将会没有leader。Raft保证在任意一个任期内,最多只有一个leader

不同的服务器节点观察到的任期转换的次数可能不同,在某些情况下,一个服务器节点可能没有看到 leader 选举过程或者甚至整个任期全程。任期在 Raft 算法中充当逻辑时钟的作用,这使得服务器节点可以发现一些过期的信息比如过时的leader 。每一个服务器节点存储一个当前任期号,该编号随着时间单调递增。服务器之间通信的时候会交换当前任期号;如果一个服务器的当前任期号比其他的小,该服务器会将自己的任期号更新为较大的那个值。如果一个 candidate 或者 leader 发现自己的任期号过期了,它会立即回到follower状态。如果一个节点接收到一个包含过期的任期号的请求,它会直接拒绝这个请求。

Raft算法中服务器节点之间使用RPC进行通信,并且基本的一致性算法只需要两种类型的RPC:

  1. 请求投票(RequestVote)RPC由 candidate 在选举期间发起
  2. 追加条目(AppendEntries)RPC由leader发起,用来复制日志和提供心跳机制

5. Leader选举

Raft 使用一种心跳机制来触发 leader 选举。当服务器程序启动时,他们都是 follower 。一个服务器节点只要能从 leader 或 candidate 处接收到有效的 RPC 就一直保持 follower 状态。Leader 周期性地向所有 follower 发送心跳(不包含日志条目的 AppendEntries RPC)来维持自己的地位。如果一个 follower 在一段选举超时时间内没有接收到任何消息,它就假设系统中没有可用的 leader ,然后开始进行选举以选出新的leader。

要开始一次选举,follower先增加自己的当前任期号并且转换到candidate状态。然后该节点投票给自己,并且并行地向集群中的其他服务器发送RequestVote RPC,要求其他服务器节点投票给它。Cancidate 会一直保持直到以下三种情况之一发生:

  1. 它自己赢得了这次选举(收到过半的选票)
  2. 其他的服务器节点成为Leader
  3. 一段时间后没有任何获胜者

如果 candidate 赢得选举,会向其他服务器节点发送心跳信号来确定自己的地位并阻止新的选举。在等待投票期间,candidate 可能会收到另一个声称自己是 leader 的服务器节点发来的 AppendEntries RPC。如果这个 leader 的任期号不小于当前的任期号,则 candidate 承认该leader并退回到follower, 否则拒绝这次RPC并继续保持 candidate 状态。

6. 日志复制

Leader 决定什么时候把日志条目应用到状态机中是安全的;这种日志条目被称为已提交的。一旦创建该日志条目的 leader 将它复制到过半的服务器上,该日志条目就会被提交。Leader 追踪将会被提交的日志条目的最大索引,未来的所有 AppendEntries RPC 都会包含该索引,这样其他的服务器才能最终知道哪些日志条目需要被提交。Follower 一旦知道某个日志条目已经被提交就会将该日志条目应用到自己的本地状态机中。

leader 通过强制 follower 复制它的日志来解决不一致的问题。这意味着 follower 中跟 leader 冲突的日志条目会被 leader 的日志条目覆盖。Leader 从来不会覆盖或者删除自己的日志条目。

日志匹配特性:

  • 如果不同日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令。
  • 如果不同日志中的两个条目拥有相同的索引和任期号,那么他们之前的所有日志条目也都相同。

Leader 在特定的任期号内的一个日志索引处最多创建一个日志条目,同时日志条目在日志中的位置也从来不会改变。该点保证了上面的第一条特性。第二个特性是由 AppendEntries RPC 执行一个简单的一致性检查所保证的。在发送 AppendEntries RPC 的时候,leader 会将前一个日志条目的索引位置和任期号包含在里面。如果 follower 在它的日志中找不到包含相同索引位置和任期号的条目,那么他就会拒绝该新的日志条目。

因此,每当 AppendEntries RPC 返回成功时,leader 就知道 follower 的日志一定和自己相同(从第一个日志条目到最新条目)。

7. 安全性

7.1 选举限制

RPC 中包含了 candidate 的日志信息,如果投票者自己的日志比 candidate 的更新,它会拒绝掉该投票请求。

Raft 通过比较两份日志中最后一条日志条目的索引值和任期号来定义谁的日志比较新。如果两份日志最后条目的任期号不同,那么任期号大的日志更新。如果两份日志最后条目的任期号相同,那么日志较长的那个更新。

7.2 提交之前任期内的日志条目

Raft 永远不会通过计算副本数目的方式来提交之前任期内的日志条目。只有 leader 当前任期内的日志条目才通过计算副本数目的方式来提交;一旦当前任期的某个日志条目以这种方式被提交,那么由于日志匹配特性,之前的所有日志条目也都会被间接地提交。

也即是,即使之前任期内的日志条目超过了半数,依然不执行,必须要等到当前任期的日志条目超过半数,才可以全部进行提交。

为什么会出现这种情况?

因为raft leader选举不完备,一个节点要当选为leader有两种情况

  • 它的term >= 多数派的 term
  • 他的term == 多数派的term,它的log长度 >= 多数派

在情况a中,新leader可能缺一些多数派中存在且已经被提交的log。

参考:

8. Follower 和 candidate 崩溃

若这两者崩溃,后续发送给他们的 RequestVote 和 AppendEntries PRCs 都会失败,Raft 通过无限的重试来处理这种失败。如果崩溃的机器重启了,这些RPC就会成功的完成。由于Raft的RPC都是幂等的,因此重试不会造成任何危害。

9. 日志压缩

快照技术是日志压缩最简单的方法。在快照技术中,整个当前系统的状态都以快照的形式持久化到稳定的存储中,该时间点之前的日志全部丢弃。每个服务器独立地创建快照,快照只包括自己日志中已经被提交的条目。

对于运行缓慢的 follower 或者新加入集群的服务器,让该 follower 更新到最新的状态的方式就是 leader 通过网络把快照发送给它。

参考Raft算法(论文翻译)

posted @ 2021-04-01 12:40  珠玑位  阅读(122)  评论(0编辑  收藏  举报