2.1 启动流程
流程详解
流程概览
在启动节点前,用户需要创建 BRPC Server,并调用
braft::add_service将 Raft 相关的 Service 加入到 BRPC Server 中,以及启动 BRPC Server用户创建
braft::Node,并调用Node::init启动节点,此后启动流程将由 braft 接管启动任务队列
ApplyTaskQueue遍历一遍日志,读取每条日志的
Header(24 字节):4.1 若
Header中的类型显示其为配置,则读取完整日志以获取配置4.2 构建索引(
logIndex到文件offset),便于读取时快速定位4.3 获得当前节点的
firstLogIndex和lastLogIndex4.4 删除
firstLogIndex之前的日志文件(快照遗留)
加载快照:
5.1 打开本地快照,并返回
SnapshotReader5.2 以
SnapshotReader作为参数回调用户状态机on_snapshot_load来加载快照5.3 等待快照加载完成,将快照元数据中的节点配置设为当前节点配置
将日志中的配置(步骤 4.1)或用户指定配置(
initial_conf)设为当前节点配置读取 Raft 元数据文件,即
term与votedFor:7.1 将节点
currentTerm设置为term7.2 恢复投票状态
votedFor
启动快照定时器
将自身角色变为
Follower,并启动选举定时器将节点加入 Raft Group
至此,启动完成,节点将等待选举超时后发起选举
从上述流程可以看出,启动工作可以大致分为以下 2 类:加载持久化存储来恢复节点状态(步骤 4、5、6、7),以及启动算法(步骤 8、9)。

Raft Service
当用户调用 braft::add_service 时,braft 会增加以下 4 个 Raft 相关的 Service 至 BRPC Server:
FileService:用于 Follower 安装快照时,向 Leader 下载快照中的文件。RaftService:核心服务,用于处理 Raft 算法,如选举投票、复制日志、下发安装快照指令等。RaftStat:可观测性的一部分,用户可通过访问http://${your_server_endpoint}/raft_stat查看当前这个进程上 Node 的列表,以及每个 Node 的内部状态,详见查看节点状态。CliService:允许用户通过发送 RPC 请求来控制节点,如配置变更、重置节点列表、转移 Leader;当然用户也可以通过 API 来控制节点。
ApplyTaskQueue
这是一个串行执行的任务队列,由 BRPC ExecutionQueue 实现,所有需要回调用户状态机的任务都会进入该队列,并被依次串行回调:
COMMITTED
当已被提交的日志,需被应用到状态机时
若日志类型为节点配置,则回调 on_configuration_committed,否则回调 on_apply
SNAPSHOT_SAVE
创建快照
on_snapshot_save
SNAPSHOT_LOAD
加载快照
on_snapshot_load
LEADER_START
当节点成为 Leader 时
on_leader_start
LEADER_STOP
当节点不再是 Leader 时
on_leader_stop
START_FOLLOWING
当 Follower 或 Candidate 开始跟随 Leader 时;此时其 leader_id 为 Leader 的 PeerId
on_start_following
STOP_FOLLOWING
当节点不再跟随 Leader;此时其 leader_id 将变为空
on_stop_following
ERROR
当节点出现出错,此时任何 apply 任务都将失败
on_error
持久化存储
Raft 拥有以下 3 个持久化存储,这些都需要在节点重启时进行重建:
RaftMetaStorage:保存 Raft 算法自身的状态数据(即
term与votedFor)LogStorage:存储用户日志以及日志元数据(即
firstLogIndex)SnapshotStorage:存储用户快照以及元数据
比较容易忽略的是,其实节点的配置也是持久化的,其会保存在快照元数据(SnapshotStorage)和日志(LogStorage)中。
Raft 元数据 StablePBMeta:
日志元数据 LogPBMeta:
快照元数据 LocalSnapshotPbMeta:
日志回放
当节点刚启动时,其不会回放日志,因为 commitIndex 并没有持久化,所以节点在启动时并不知道自己的 commitIndex,也就不知道该 apply 哪些日志。只有当集群产生 Leader 后集群中的节点才开始回放日志,Leader 的 commitIndex 由其当选 Leader 后,提交一条本任期 no-op 日志后确定,其 commitIndex 就等于该 no-op 日志的 index,而 Follower 的 commitIndex 由 Leader 在之后的心跳或 AppendEntries 请求中告知,详见 3.1 选主流程。
而对于快照来说,其代表的都是 applied 的数据,所以可以安全的加载。
节点配置
节点在启动时,其配置取决如下:
优先读取日志中的配置
若当前节点不存在日志或日志中没有配置,则读取快照元数据中保存的配置
若当前节点为新节点,既没有日志,也没有快照,则使用用户指定的
initial_conf若用户没有指定配置,则该节点配置为空
从以上流程可以看出,只有当节点以空节点启动时,用户指定的配置才会生效。
具体实现
braft:add_service
用户需调用 braft::add_service 将 braft 相关 Service 加入到 BRPC Server 中:
braft::add_service 会调用 NodeManager::add_service 来增加以下 4 个 Service:
braft::Node
此外,用户在启动节点前,需要构建一个 braft::Node:
GroupId:一个字符串, 表示这个复制组的
IDPeerId:结构是一个 EndPoint,表示对外服务的端口,外加一个 Index (默认为 0)用于区分同一进程内的不同副本
Node::init
最后,用户需调用 Node::init 来启动节点,在 init 函数中主要完成以下几项工作:
参考
Last updated