Raft 简介
共识是分布式容错系统的基础问题,是指多个服务器对值达成一致。
raft 设计目的:
- 易于理解
- 跟 Paxos 有同等容错能力和性能
raft 服务器状态:
- 领导(leader):处理所有客户端请求
- 跟随者(follower):被动响应候选人和领导
- 候选人(candidate):中间状态,在选举时出现
领导选举(Leader election)
terms(任期)
- 所有的服务器管理着当前任期:
- 任期单调递增
- 作为逻辑时钟管理
- 无论服务器在何时进行通信,任期会进行交换
- 任期交换规则:
- 当 A 的任期大于 B 的任期时,B 更新任期为 A 的
- 当 A 的任期大但 B 认为自己才是领导时,B 回归跟随者状态
- 当 A 的任期大但收到来自 B 的请求时,A 拒绝 B 的请求,B 更新任期后再发出请求
跟随者状态
- 所有服务器都是从跟随者状态开始
- 所有服务器都有本地的计时器
- 只要在计时内能收到来自领导或候选人的消息(心跳消息),就一直保持跟随者状态
- 当计时内未收到心跳消息,则认为系统没有存活的(viable)领导,开始选举流程,自提名(nominate)为候选人
如何解决多个跟随者自提名为候选人的问题?各个服务器使用随机的计时超时
候选人状态
- 跟随者进入后续人状态过程:增加当前任期,并设置状态为候选人
- 给自己投一票,然后再发送投票请求到其他服务器
- 其他服务器收到投票请求后,会增加任期,然后投票
- 候选人计票,当得到大多数选票后,转为领导状态,并发送心跳消息;若落选,退出到跟随者状态。
日志复制(Log replication)
日志:
- 每个节点都维护着一份操作日志(action log)
- 日志中的每一条目(entry)包括:操作和任期(action and term),任期表示节点接收到操作的时间。
复制过程:
- 当客户端请求进入领导节点时(rd<-Ready),领导节点先追加条目到自身的日志中(storage append rd.Entrieds)
- 接下来,领导节点告知其他节点追加条目,并等待确认追加( transport send rd.Messages)
- 正如选举过程,复制过程也是遵循少数服从多数的原则,当收到大多数节点的确认时,领导会提交条目(commit entry),然后更新状态机,再回复成功消息给客户端和其他节点。(applyConfChange & advance)
如果跟随者失败了,领导会不断尝试去追加,一旦跟随者复活,条目会被最终提交。
如果领导失败了,那么条目将不会被提交,进入下个任期选举,并产生新的条目。
etcd-raft
etcd/raft 中的心跳和选举周期都是通过逻辑时钟控制的,一般设置心跳周期为1 个逻辑时钟,选举周期为 10 个逻辑时钟。接入 etcd/raft 时,需要自定义一个 Ticker,然后在 tick 之后,以心跳为例,内部会将 heartbeatElapsed 自增,然后判断是否到达设定的 Timeout,到达则恢复 heartbeatElapsed 为 0,并执行心跳操作。
参考
https://db.cs.duke.edu/courses/compsci512/spring15/lectures/raft-guest.pptx