kikimo

Raft 算法笔记(四)——集群节点变更

  1. 集群节点变更需要保证

    • 任意时刻最多只有一个主节点
    • 主节点完备性
  2. 直接变更集群节点无法保证任意时刻只有一个主节点,例如:

    • 旧节点 o1、o2、o3
    • 新节点 n4、n5
    • o3 知悉 n4、n5 且 o3、n4、n5 选出一个主节点
    • o1、o2、o3 选出另外一个主节点,违背主节点唯一的性质
  3. 联合共识(joint consensus)

    • 联合指 C(old) 和 C(new) 配置的联合,用 C(old, new) 表示
  4. 配置的两阶段提交算法

    • 联合共识生效阶段
    • 新配置生效阶段
  5. 联合共识阶段的规则

    • 所有日志需要同时提交到 C(old) 集群和 C(new) 集群
    • C(old) 和 C(new) 集群中的节点都可以当选主节点
    • 当选的主节点必须同时获得 C(old) 和 C(new) 的多数票
      • 这一要求保证了系统不会在一个 term 内同时出现两个主节点
  6. 联合共识阶段允许节点在任意时刻执行配置变更,而且可以继续为客户端提供服务,那么问题来了:

    • transition between configurations 指的是 C(old) -> C(old, new)
  7. 配置变更过程

    • 节点收到 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(new) 记录也是立即生效
  8. 为什么 C(old, new) 提交后还需要在提交一条 C(new) 日志?

假设不提交 C(new) 日志。虽然 C(old, new) 已经被提交,但是可能只有主节点知道。 如果我们成功吧 C(old) - C(new) 中的节点下掉时,leader 崩了, 这时其他节点上目前生效的还是 C(old, new) 配置,就可能出现无法选主的情况, 因为无法获得 C(old) 节点集合中超过半数的投票(他们都被下掉了)。 如果我们等 C(new) 提交后在下掉几点,这时候集群中超过半数的节点上生效的都是 C(new) 配置, 不用担心集群崩了无法选主的情况。

  1. 新节点的初始化时间节点

    • 初始化只同步主节点上的日志
    • 新节点在配置生效之前需要先完成初始化
    • 这个配置指联合共识的配置 C(old, new)
  2. 已移除节点对新配置集群的干扰

    • 节点如何移除?
      • 扩展 RequestVote RPC 来通已被移除的节点?
    • 干扰可能发生在什么时间点
      • C(new) 在 leader 节点上生效后不会再个将被移除的节点发 AppendEntries 消息,从这个时间点开始后节点真正被移除后,他们可能会发起扰乱集群的 RequestVote RPC
  3. 主节点不在新配置中

    • 这时候当 C(new) 配置提交后,主节点把自己变成 follower(然后等着被踢?)
    • 这时候主节点在提交记录的时候不能把自己的 replica 也算在内,因为它马上被踢掉了,如果不这样它被踢掉后之前提交的 log 的备份可能不超过半数