Raft Scheduler 概述
Raft Scheduler 在节点初始化时会一起启动,同时还会一起启动 raftTickLoop 过程用于定时产生 tick 申请。
Raft Scheduler 次要的性能是解决 raftGroup 外部的音讯申请与内部的写入读取申请,Raft Scheduler 的工作形式是通过对外提供一个 rangeID 队列,当有申请来时,会批改该申请设计的 range 状态,并将其放入这个队列中,同时当 Raft Scheduler 追随云溪数据库启动而初始化时,会初始化肯定数量的 processor,这些 processor 的作用是监听音讯队列,当音讯队列中有 rangeID 入队时,processor 会将该 rangeID 取出,并依据该 rangeID 入队时的状态 (stateTick、stateReady、stateRequest) 进行不同解决。
咱们在后续优化云溪数据库版本的过程中将 RaftScheduler 额定划分了一部分资源用于解决 tick 申请从而升高心跳提早,这并不影响咱们了解整体的逻辑解决流程。
Request 入队流程
在正本层中须要解决的数据来自于散发层通过 gRPC 发送来的 RaftMessageBatch,以及外部产生的音讯申请。
当接管到散发层的 RaftMessageBatch 后,音讯解决会通过 store 获取 RaftMessageRequest 对应 rangeID 的 raftRequestQueue,并将 RaftMessageRequest 转换成 raftRequestInfo 追加至 raftRequestQueue 中。与此同时,更新 rangeID 对应的 raftScheduleState 并将 rangeID 增加到 raftScheduler 的 queue 中期待 raft processor 调度解决。
StateRaftTick 解决流程
如果 processor 从 rangeIDQueue 中取出的 rangeID 对应状态为 stateRaftTick,则 processor 会调用 ProcessTick () 办法进行解决。
首先,processor 会在 store 中获取该 rangeID 的本地 replica 和对应 livenessMap, 而后调用 replica_raft.go 下 tick ()。获取 replica 的 unreachable remotes,结构 MsgUnreachable, 通过 replica 所在的 raft group 解决该 msg。完结这一步后,会验证 replica 是否为静默状态,如果 replica 是静默状态,则无需 tick。Tick 的条件为:
A. raft group 已初始化。
B. replica 是非静默的。
C. 再次查看 replica 是否静默,静默的判断条件为满足以下所有条件:
- cluster 启用静默配置。
- replica 不存在尚未利用的 raft command。
- replica 不存在进行中的 merge。
- replica 没有被 destroyed。
- replica 所在 raft group 状态非空。
- replica 是 raft leader。
- replica 以后不存在 raft leader 切换。
- replica 是 leaseholder。
- raft 的 applied/commit/lastindex 相等。
- 获取所有 remote replica,除了不在 livenessMap 中的 node,remote replica 在 leader 生成的 progress 列表中,同时 remote replica 的 progress match 值必须跟以后 raft 的 applied 相等。
- replica 自身必须在 progress 列表中。
- raft 以后状态不是 ready。
如果判断以后 replica 所在 raft group 是静默状态,只发送心跳数据:
首先,获取 range 中其余 remote replica 的 ReplicaDescriptor,为每个 remote replica 结构 raftpb.MsgHeartbeat, 结构 StoreIdent 和 RaftHeartbeat, 生成 KV 并写入 store 的 heartbeats map < 指标 store 标识,心跳数据 > 中,在 store.go->coalescedHeartbeatsLoop () 解决 store 的心跳数据,对每个 KV 结构 RaftMessageRequest 并写入对应节点的 channel 中,在 raft_transport.go->startProcessNewQueue ()->processQueue () 中保护该 channel,负责将 request 转发到指定 replica。
如果判断以后 replica 所在 raft group 是非静默状态,除进行上述操作外,在这之后会查看以后 replica 是否同时为 raft leader 和 leaseholder (DisableLeaderFollowsLeaseholder 为 false 时的要求),如果不满足,会结构 leader 切换的 message,发动一轮选举。
– StateRaftReady –
如果 rangeID 对应的状态是 stateRaftReady。
- store.go->processReady () 首先从 store 中获取该 rangeID 对应的本地 replica,通过 relica 的 internalRaftGroup 结构 raft Ready 数据 (raft Ready 蕴含了须要长久化的 entries 和须要提交或发送到其余节点的 message),而后将 Ready 中蕴含的 msg 分为 MsgApp 和其余类型两局部,首先将 MsgApp 类型音讯结构成 RaftMessageRequest 写入到对应 node 的 channel 中,而后通过 raft_transport.go->startProcessNewQueue ()->processQueue () 转发到指定 replica。
- 在异步发送 msg 过程中,会通过 rocksDB/Pebble 结构 batch,同时解决 Ready 中蕴含的待长久化的 entries,并结构 repr,而后应用结构的 batch 将 repr 异步迭代写入到 store 对应的存储引擎中。
- 将拆分出的其余音讯类型结构 RaftMessageRequest 发送到 node 的 channel 中。
- 从 Ready 中蕴含的 CommittedEntries 每一行中获取 command id 和 raft command 并生成 WriteBatch, 结构 WriteBatch, 并生成 Repr,将 Repr 中数据异步落盘。
– StateRaftRequest –
如果 rangeID 对应的状态是 stateRaftRequest,从 store 中获取该 rangeID 对应的 raftRequestQueue,而后获取队列中的每个 raftRequestInfo, 每个 raftRequestInfo 中的 RaftMessageRequest 都带有一条 message,依据每条 message 的 type 不同有不同的解决办法,可参考上述 Msg 类型篇。
- MsgHub,当 follower 节点的选举计时器超时后,会发送 msgHub.
- MsgBeat,leader 发送的心跳信息,心跳计时器超时时触发该音讯,leader 通过 stepLeader () 生成 MsgHeartbeat 发送给集群中其余节点。
- MsgProp,客户端向集群发送的写申请通过 msgProp 示意。
- MsgApp,当一个节点通过选举成为 leader 后,会获取指标节点 Next-1 对应的记录的 term 值和须要发送的 Entries, 而后发送 MsgApp 音讯,该音讯能够帮忙 follower 节点与 leader 节点同步。
- MsgAppResp,msgApp 的响应音讯类型,当 follower 节点收到 msgApp 后,无论是否进行日志追加,都将返回一条带有本节点最初一条记录索引值的音讯。
- MsgVote,当 PreCandidate 状态节点收到半数以上的投票之后,会发动新一轮的选举,即向集群中的其余节点发送 MsgVote。
- MsgVoteResp,msgVote 的响应音讯。
- MsgSnap,当 leader 获取指标节点 Next-1 对应的记录的 term 值和须要发送的 Entries 出现异常时,就会生成 msgSnap 将快照数据发送到 follower 节点,follower 节点通过快照数据恢复状态,从而能够与 leader 进行失常的 entry 记录复制。
- MsgHeartbeat,leader 发送的心跳音讯,次要作用是探测节点是否存活,follower 接管到 msgHeartbeat 会重置本身的选举计时器,避免 follower 发动新一轮的选举。同时尝试更新 follower 节点 raftLog 中已提交的地位。
- MsgHeartbeatResp,follower 解决心跳音讯返回的音讯类型。
- MsgUnreachable,如果 leader 发送 MsgSnap 音讯出现异常,将会调用 ReportUnreachable () 发送该类型音讯,将 follower 节点的状态改为 ProgressStateProbe。
- MsgSnapStatus,校验节点对应的状态是否为 ProgressStateSnapshot,如果之前发送的快照音讯出现异常则将节点状态改为 ProgressStateProbe,之后单条发送音讯。
- MsgCheckQuorum,leader 发送该音讯类型检测是否放弃半数以上连贯。当 Leader 的心跳计时器超时,并且开启了 checkQuorum 模式 (raft 的 checkQuorum 字段为 true)。该 Leader 节点就会发送 MsgCheckQuorum 音讯检测与集群中其余节点是否放弃半数以上的连贯,如果没有则变成 Follower 节点。
- MsgTransferLeader,发动 leader 节点转移的音讯类型,本地音讯。
- MsgTimeoutNow,如果 leader 节点转移超时,会发送该类型的音讯,使 follower 的选举计时器立刻过期,并发动新一轮的选举。
- MsgReadIndex,客户端发往集群的只读音讯应用该类型。
- MsgReadIndexResp,只读音讯类型的响应音讯。
- MsgPreVote,当 Follower 的选举计时器超时时,会把以后状态切换成 StatePreCandidate(预选举),并向集群中其余节点发送 MsgPreVote。当集群中其余节点收到预选举音讯时,会先进行一些测验,合乎相干条件会投批准,否则会投回绝票,而后发送给该候选节点,发送的音讯类型为 MsgPreVoteResp。如果预选举阶段 (StatePreCandidate) 胜利收到超过半数以上的批准票,那么该节点会认为选举胜利,会发动新一轮的正式选举 (节点状态切换成 SateCandidate ( 候选人),发送的音讯类型为 MsgVote)。是否有预选举阶段是依据初始化配置的参数,该字段保留在 raft 构造体的 preVote 字段中
- MsgPreVoteResp,其余节点响应预选举的投票音讯。