Raft 算法笔记(四)——集群节点变更
-
集群节点变更需要保证
-
直接变更集群节点无法保证任意时刻只有一个主节点,例如:
- 旧节点 o1、o2、o3
- 新节点 n4、n5
- o3 知悉 n4、n5 且 o3、n4、n5 选出一个主节点
- o1、o2、o3 选出另外一个主节点,违背主节点唯一的性质
-
联合共识(joint consensus)
- 联合指 C(old) 和 C(new) 配置的联合,用 C(old, new) 表示
-
配置的两阶段提交算法
-
联合共识阶段的规则
- 所有日志需要同时提交到 C(old) 集群和 C(new) 集群
- C(old) 和 C(new) 集群中的节点都可以当选主节点
- 当选的主节点必须同时获得 C(old) 和 C(new) 的多数票
- 这一要求保证了系统不会在一个 term 内同时出现两个主节点
-
联合共识阶段允许节点在任意时刻执行配置变更,而且可以继续为客户端提供服务,那么问题来了:
- transition between configurations 指的是 C(old) -> C(old, new)
-
配置变更过程
- 节点收到 C(old, new) 后会立马生效——暨它会立即使用 C(old, new) 作为最新的配置
- 如果在 C(old, new) 提交前 leader crash 了,这时候使用的不是 C(old) 就是 C(old, new) 配置
- 使用 C(old) 配置会不会有什么问题,比如,C(new) 中的机器并不知道谁当选了 leader?
- 当 C(new) 中的节点发送 RequestVote RPC 时,收到 reply 就知道谁是 leader 了!
- C(old, new) 记录提交失败时,客户端最终需要重新提交变更请求。
- 一旦 C(old, new) 被提交后 C(old)、C(new) 都没法单独生效
- 因为 C(old, new) 在过半数的节点上,只有存在 C(old, new) 的节点能当选主节点,所以说此时 C(old) 和 C(new) 都无法单独生效
- C(old, new) 记录提交后 leader 节点 append 一条包含 C(new) 的 log
-
为什么 C(old, new) 提交后还需要在提交一条 C(new) 日志?
假设不提交 C(new) 日志。虽然 C(old, new) 已经被提交,但是可能只有主节点知道。
如果我们成功吧 C(old) - C(new) 中的节点下掉时,leader 崩了,
这时其他节点上目前生效的还是 C(old, new) 配置,就可能出现无法选主的情况,
因为无法获得 C(old) 节点集合中超过半数的投票(他们都被下掉了)。
如果我们等 C(new) 提交后在下掉几点,这时候集群中超过半数的节点上生效的都是 C(new) 配置,
不用担心集群崩了无法选主的情况。
-
新节点的初始化时间节点
- 初始化只同步主节点上的日志
- 新节点在配置生效之前需要先完成初始化
- 这个配置指联合共识的配置 C(old, new)
-
已移除节点对新配置集群的干扰
- 节点如何移除?
- 扩展 RequestVote RPC 来通已被移除的节点?
- 干扰可能发生在什么时间点
- C(new) 在 leader 节点上生效后不会再个将被移除的节点发 AppendEntries 消息,从这个时间点开始后节点真正被移除后,他们可能会发起扰乱集群的 RequestVote RPC
-
主节点不在新配置中
- 这时候当 C(new) 配置提交后,主节点把自己变成 follower(然后等着被踢?)
- 这时候主节点在提交记录的时候不能把自己的 replica 也算在内,因为它马上被踢掉了,如果不这样它被踢掉后之前提交的 log 的备份可能不超过半数