介绍
分布式一致性算法允许多个机器作为一个整体来运行服务,即使部分机器出了问题,服务也可以正常进行。其起源于 replicated state machines,结构如下
分布式一致性算法核心作用是保持请求 log 的一致,每个状态机通过执行完全相同、顺序一致的日志来保持一致性。
raft 基础
raft是由多个服务组成,服务之间的调用是通过 RPCs 进行。
状态
raft集群中每个服务处于的状态可以是如下三种之一:
- leader
- follower
- candidate
在一个正常的情况下,集群中只有一个是 leader,其他的是 follower。follower 是被动的,只接受请求并对请求做回应。leader接受所有客户端的请求,并分发给 follower。candidate 是用于选举新的 leader 的。三种状态转变的情况如下
任期
raft将时间划分为 term(任期),如下图所示。每个 raft开始都是一次选举,选举时间内通常会有一个或者多个 candidate竞选leader ,如果一个 candidate 赢得了选举,那么在term剩下的时间内就会作为 leader 进行服务。在一些场景下,term 内的选票被划分,导致没有 candidate 赢得选举成为 leader,此时会开启一个新的 term,进行新一轮的选举。
term在 raft中的作用和时钟周期似的,每个服务都会保存一个当前的 term。服务之间进行调用的时候,会附带term;如果一个服务发现其保存的 term较小,就会更新为较大的。如果 leader 或者 candidate 收到比其大的term的请求,就会更新term 并且转换状态为 follower。如果一个服务接收到较小的 term 的请求,则会拒绝这个请求。
Leader 选举
raft 通过心跳的机制来触发选举。服务在接受到有效的(term 大于等于自身的 term) 来自于 leader 或者 candidate 请求的时候会一直处于 follower 状态。leader 会周期性的发送请求给 follower 来保持其权威性的状态。如果一个 follower 在 election timeout 的时间范围内没有收到 leader 的请求,则认为没有有效的 leader,会开启新一轮的选举。
follower 在开启选举的时候会对其 term 进行加一,并修改状态为 candidate。然后给自己投一票,并对其他服务发送投票请求。如果一个 follower 收到投票的个数大于等于n/2 + 1(n为服务个数) ,则其赢得选举,成为leader。
投票的规则:每个服务在一个 term 内,至多为一个 candidate 投票, follower 会为第一个到来的符合条件的投票请求投赞同票。candidate 发送的投票请求会附带两个参数,其最新一条日志的 index 和 term。在参数 term 大于等于 follower 最新一条日志的 term,并且参数 index(日志不仅仅有term属性,还有index属性,表示 log 在term内的位置) 大于等于 follower 最新一条日志的 index 的时候,会对其投赞同票,否则投否定票。
投票的结果又三种可能性
- 此
candidate赢得选举,成为leader - 其他的
candidate赢得选举成为leader(在投票的过程中,收到大于等于此term内的leader发送的添加日志的请求,则转变candidate状态为follower) - 此
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)服务所接受。这也意味着之前的日志也是安全的(leader 中 index小于当前commited日志index的日志也是安全的)
结论
到此为止,简单的讲了raft 的基本概念,leader的选举,以及log的复制。虽然看起来比较简单,但是这些基本的概念是否足以保证安全呢?这个还是有待商榷的,后续会对安全行为进行讲解。
参考
本文主要参考如下文章