raft 分布式一致性算法 1

414 阅读5分钟

介绍

分布式一致性算法允许多个机器作为一个整体来运行服务,即使部分机器出了问题,服务也可以正常进行。其起源于 replicated state machines,结构如下

分布式一致性算法核心作用是保持请求 log 的一致,每个状态机通过执行完全相同、顺序一致的日志来保持一致性。

raft 基础

raft是由多个服务组成,服务之间的调用是通过 RPCs 进行。

状态

raft集群中每个服务处于的状态可以是如下三种之一:

  1. leader
  2. follower
  3. candidate

在一个正常的情况下,集群中只有一个是 leader,其他的是 followerfollower 是被动的,只接受请求并对请求做回应。leader接受所有客户端的请求,并分发给 followercandidate 是用于选举新的 leader 的。三种状态转变的情况如下

任期

raft将时间划分为 term(任期),如下图所示。每个 raft开始都是一次选举,选举时间内通常会有一个或者多个 candidate竞选leader ,如果一个 candidate 赢得了选举,那么在term剩下的时间内就会作为 leader 进行服务。在一些场景下,term 内的选票被划分,导致没有 candidate 赢得选举成为 leader,此时会开启一个新的 term,进行新一轮的选举。

termraft中的作用和时钟周期似的,每个服务都会保存一个当前的 term。服务之间进行调用的时候,会附带term;如果一个服务发现其保存的 term较小,就会更新为较大的。如果 leader 或者 candidate 收到比其大的term的请求,就会更新term 并且转换状态为 follower。如果一个服务接收到较小的 term 的请求,则会拒绝这个请求。

Leader 选举

raft 通过心跳的机制来触发选举。服务在接受到有效的(term 大于等于自身的 term) 来自于 leader 或者 candidate 请求的时候会一直处于 follower 状态。leader 会周期性的发送请求给 follower 来保持其权威性的状态。如果一个 followerelection timeout 的时间范围内没有收到 leader 的请求,则认为没有有效的 leader,会开启新一轮的选举。

follower 在开启选举的时候会对其 term 进行加一,并修改状态为 candidate。然后给自己投一票,并对其他服务发送投票请求。如果一个 follower 收到投票的个数大于等于n/2 + 1(n为服务个数) ,则其赢得选举,成为leader

投票的规则:每个服务在一个 term 内,至多为一个 candidate 投票, follower 会为第一个到来的符合条件的投票请求投赞同票。candidate 发送的投票请求会附带两个参数,其最新一条日志的 indexterm。在参数 term 大于等于 follower 最新一条日志的 term,并且参数 index(日志不仅仅有term属性,还有index属性,表示 log 在term内的位置) 大于等于 follower 最新一条日志的 index 的时候,会对其投赞同票,否则投否定票。

投票的结果又三种可能性

  1. candidate 赢得选举,成为 leader
  2. 其他的 candidate 赢得选举成为 leader (在投票的过程中,收到大于等于此 term 内的 leader 发送的添加日志的请求,则转变 candidate 状态为 follower)
  3. term 内没有 candidate 赢得选举成为 leader

第三种可能性是由于多个 follower 同时变为 candidate ,投票被多个 candidate 瓜分,导致没有candidate 获得大部分投票,即没有 candidate 赢得选举成为 leader。为了避免这种情况持续的发生,raft 使用随机的 election timeout (减小了多个 follower同是变为 candidate 的可能性)。

日志复制

当一个 leader 确立了之后,就会接受客户端发送的请求。每个客户端发送的请求都包含一个对 state machine 执行的操作。收到请求后,leader会将该操作添加到本地的日志中去,然后将日志同步给其他服务。当日志被安全(后面会介绍)的复制之后,会把此日志对应的操作应用于状态机,并返回给客户端操作执行成功的响应,最后发送给其他服务,表示此操作已经被状态机执行(这个步骤可以附加在复制日志的请求之中)。

日志的组织格式如下图所示(相同颜色的表示位于一个term内):

leader 会决定在安全的时候,应用操作日志到 state machine 之中,这种操作日志的状态称为 committed。raft 保证 committed的日志会持久化,并会被其他有效的服务得到执行。所谓的安全就是日志被大部分(大于等于n/2+1)服务所接受。这也意味着之前的日志也是安全的(leaderindex小于当前commited日志index的日志也是安全的)

结论

到此为止,简单的讲了raft 的基本概念,leader的选举,以及log的复制。虽然看起来比较简单,但是这些基本的概念是否足以保证安全呢?这个还是有待商榷的,后续会对安全行为进行讲解。

参考

本文主要参考如下文章

  1. raft.github.io/raft.pdf