tl;博士
即使您使用 Zookeeper、Consul 或 etcd 等注册中心之一实施 master 选举,似乎总是存在一种竞争条件,即旧 master 没有意识到它不再是 master 并尝试写入,而新 master 是写入畅通,导致同时写入两个服务实例,这是我们想要避免的。我们如何在没有这种竞争条件的情况下实现主选举?
详细的问题陈述
假设我们想使用其中一个注册中心(例如 Zookeeper、Consul 或 etcd)来实现主节点选举以进行故障转移。
假设服务 S1、S2、S3 有 3 个实例,每个实例在同一台机器上都有对应的注册节点,当前 S1 为主,S2 和 S3 为从。
此外,S1、S2、S3 都将共享状态存储在注册表中,但我们不希望多个实例同时写入该状态,因为并发访问可能会使状态不一致。
假设 S1 正在对存储在注册表中的共享状态进行写操作。也就是在再次检查是否是leader之前会执行更多的写操作。
假设此时有一个网络分区。在一个分区上是 S1。在另一个分区上是 S2 和 S3,所以它有仲裁。
注册中心正确地将 S2 识别为新的领导者,并使 S1 作为领导者无效。
S2 激活是因为它是新的领导者,并开始对存储在注册表中的共享状态进行一系列写入操作。
分区已修复。
此时 S1 和 S2 都会同时对注册表执行写操作,并且由于分区已修复,它们的写操作都会成功,这可能会导致状态不一致。
示例跟踪是:
- S1 被告知它是主控,并开始对注册表中的共享状态进行写入操作
- 发生分区,S1 在一个分区中,S2 和 S3 在另一个分区中
- S2 被识别为新的 master
- S2 被通知它是新的主控,并开始对注册表中的共享状态进行写入操作
- 分区已修复
- S1 写入注册表中的共享状态并成功,因为没有分区
- S2 写入注册表中的共享状态并且也成功,导致写入与 S1 的任意交错
- S1 被通知它不再是主控并停止写入
想法
- 考虑到 Consul 的会话,一个 API 写入调用是否也需要一个会话 ID 并且只有在该会话 ID 仍然是 master 的情况下才能成功解决这个问题?
- 领事或其他注册机构之一是否有这样的电话?