介绍
分布式一致性算法允许多个机器作为一个整体来运行服务,即使部分机器出了问题,服务也可以正常进行。其起源于 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
的复制。虽然看起来比较简单,但是这些基本的概念是否足以保证安全
呢?这个还是有待商榷的,后续会对安全行为进行讲解。
参考
本文主要参考如下文章