上一篇文章我们介绍了多副本状态机,Raft就是一个维护着多副本日志的多副本状态机。
Raft首先会选举出一个唯一的Leader, Leader负责管理日志,所用对日志的添加和状态变化操作都通过Leader完成。Leader接受用户的日志请求并将日志分发给系统中其他节点。并告知其他节点何时可以安全地将日志应用到状态机上。这种方式简化了多副本日志的管理,日志的数据流向是从Leader到其他节点,而其他节点不会发送日志给Leader,非常简单。Leader当掉时,Raft会选出一个新的Leader。
通过采用Leader, Raft把一致性问题分解为相互独立的3个子问题:
Leader选举:系统初始化时需要选出Leader, Leader当掉时选出新的Leader。
日志分发:Leader接受日志请求并分发给其他节点。
确保正确性:确保所有日志副本是一致的。
本文只介绍Leader选举过程,其他两个子问题我会在后面的文章中介绍。
节点的状态与日志
在Raft集群中,任何一个节点在任何一个时刻都处于三种状态之一:Leader, Follower, Candidate。在系统正常运行的绝大部分时间,系统中会有一个Leader, 其他都是Follower。由Leader接受用户的请求,每个用户请求视为一项日志。在Raft语境下,我们所说的日志并不是系统运行的错误,警告,Debug信息等,而是指操作记录。例如提供键值对存储的Raft集群,日志是指 "设置A=100", "设置B=3", "删除C"这种引起状态变化的键值对的操作, 而查看A的值并不是日志,因为它不会引起系统状态的变化。Follower是被动的,只接受来自Leader和Candidate的请求,自己不会发出任何请求。Candidate状态是在Leader选举过程中的临时状态。下图描述了3中状态之间的转换关系:
任期
Raft把时间分为连续的任期(term),每个任期可以是任意时长,任期用连续的整数进行标号。每个任期首先进行Leader选举,选举时,多个Candidate竞争成为Leader,一旦某个节点成为Leader,其他节点则变回Follower,成为Leader的节点将在该任期内一致担任Leader,如果该Leader节点发生故障,其他节点会在新的任期内进行选举。任何一个任期内都不会有多个Leader。Raft系统中,任期是一个及其重要的概念,每个节点都维护着当前任期的值,每次节点间的通信都包含任期信息,每个节点在检测到自己的任期值低于其他节点,都会更新自己的任期值,设置为检测到的较高的值。当Leader和Candidate发现自己的任期低于别的节点,则立即把自己转换为Follower。
RPC
Raft节点间的通信采用RPC调用, Raft算法核心部分只需要用到两个RPC: RequestVote和AppendEntries。RequestVote RPC由Candidate发起用于Leader选举。AppendEntries RPC由Leader发起,用于心跳和日志复制。而Follower不会发起任何RPC。
Leader选举过程
Raft采用心跳机制触发Leader选举。当系统启动时,所有节点初始化为Follower状态,设置任期为0,并启动计时器,计时器超时后,Follower节点转化为Candidate节点,一旦转化为Candidate节点,立即开始一下几件事情:
1.增加自己的任期数
2. 启动一个新的计时器
3. 给自己投一票
4.向所有其他节点发送RequestVote RPC请求,并等待其他节点回复。
如果在计时器超时前接收到多数节点的同意投票,则转换为Leader。如果接受到其他节点的AppendEntries心跳RPC,说明其他节点已经被选为Leader, 则转换为Follower。如果计时器超时时还没有接受到以上两种信息中的任何一种,则重复步骤1-4,进行新的选举。
节点在接受到多数节点的投票成为Leader后,会立即向所有节点发送AppendEntries 心跳RPC。所有Candidate收到心跳RPC后,转换为Follower,选举结束。
每个Follower在一个任期内只能投一票,采取先到先得的策略。每个Follower有一个计时器,在计时器超时时仍然没有接受到来自Leader的心跳RPC, 则转换为Candidate, 开始请求投票。也就是在当期Leader当掉后,就会有Follower开始转换为Candidate开始投票。
如果多个节点同时发起投票,每个节点都没有拿到多数票(这种情况成为Split Vote),则增加任期数,在新的任期内重新进行投票。有没有可能Split Vote反复发生,永远都选不出Leader呢?
Raft采取随机超时时间,Raft系统有一个选举超时配置项,Follower和Candidate的计时器超时时间每次重新计算,随机选取配置时间的1倍到2倍之间。即使所有节点同时启动,由于随机超时时间的设置,各个节点一般不会同时转为Candidate,先转为Candidate的节点会先发起投票,从而获得多数票。因而在每个任期内,多个节点同时请求投票并且都只获得少数票的几率很小,连续多次发生这种情况几率更小,理论上几率很小,实际上可以认为完全不可能发生。在我的Raft实现中,基本上都在1-2个任期内选出Leader。
下一篇文章我将详细介绍Raft RPC RequestVote 和AppendEntries。