另外,为什么我需要 Zookeper 的奇数(比如 3)来选举领导者而不是偶数(比如 2)?
在像 zookeeper 这样的基于仲裁的系统中,领导者选举需要“集合”中的简单多数 - 即形成 zK 集群的节点。因此,对于一个 3 节点集成,如果剩余的两个节点要形成一个新的集成并保持运行,则可以容忍一个节点故障。另一方面,在一个四节点集成中,您需要至少 3 个节点存活才能形成多数,因此它只能容忍 1 个节点故障。另一方面,一个五节点集成可以容忍 2 个节点故障。
现在您看到 3 节点或 4 节点集群只能有效地容忍 1 个节点故障,因此拥有奇数个节点以最大化给定集群可能停机的节点数是有意义的。
zK 领导者选举依赖于一个类似于 Paxos 的协议,称为ZAB。每次写入都会经过领导者,领导者会生成一个事务 id(zxid)并将其分配给每个写入请求。id 表示在所有副本上应用写入的顺序。如果领导者收到多数人的确认,则认为写入成功。ZAB的解释。
我的问题是为什么需要 f+1 个副本的 quorum(majority) 来选举一个新的领导者?为什么不选择 f+1 in-synch-replica(ISR) 中的任何副本?为什么我们需要选举而不是简单的任何选择?
至于为什么选择选举而不是选择——一般来说,在一个具有最终一致性的分布式系统中,你需要进行一次选举,因为没有简单的方法可以知道剩余节点中哪些节点有最新的数据,从而有资格成为领导者.
在 Kafka 的情况下——对于具有多个副本和 ISR 的设置,可能有多个节点具有领导者的最新数据。
Kafka 仅将 zookeeper 用作领导者选举的推动者。如果 Kafka 分区领导者宕机,Kafka 集群控制器会通过 zK 获知这一事实,并且集群控制器会选择 ISR 中的一个作为新的领导者。所以你可以看到,这种“选举”与 zK 等基于 quorum 的系统中的新领导者选举不同。
ISR 中的哪个经纪人是“选择”的,这有点复杂(请参阅)-
每个副本都将消息存储在本地日志中,并在日志中维护一些重要的偏移位置。日志结束偏移量 (LEO) 表示日志的尾部。高水位线 (HW) 是最后提交的消息的偏移量。每个日志都会定期同步到磁盘。刷新偏移量之前的数据保证保存在磁盘上。
因此,当一个领导者失败时,一个新的领导者将通过以下方式选出:
- ISR 中每个幸存的副本都会在 Zookeeper 中注册自己。
- 首先注册的副本成为新的领导者。新领导者选择其日志结束偏移量(LEO)作为新的高水位线(HW)。
- 每个副本都会在 Zookeeper 中注册一个侦听器,以便将任何领导者更改通知它。每次通知副本有新的领导者:如果副本不是新的领导者(它必须是追随者),它会将其日志截断到其硬件,然后开始追赶新的领导者。领导者一直等待,直到 ISR 中所有幸存的副本都赶上或经过配置的时间。领导者将当前的 ISR 写入 Zookeeper 并打开自己的读写权限。
现在,与仲裁模型相比,您可能会体会到主备份模型的好处——使用上述策略,具有 2 个 ISR 的 Kafka 3 节点集群可以同时容忍 2 个节点故障——包括领导者故障——并且仍然选出一个新的领导者(尽管新的领导者将不得不拒绝新的写入一段时间,直到其中一个失败的节点上线并赶上领导者)。
付出的代价当然是更高的写入延迟 - 在具有 2 个 ISR 的 3 节点 Kafka 集群中,领导者必须等待来自两个跟随者的确认,才能确认对客户端(生产者)的写入。而在仲裁模型中,如果其中一个追随者确认,则可以确认写入。
因此,根据用例,Kafka 提供了在延迟上交易持久性的可能性。2 个 ISR 意味着您有时具有更高的写入延迟,但更高的持久性。如果你只使用一个 ISR 运行,那么万一你失去了领导者和一个 ISR 节点,你要么没有可用性,要么你可以选择不干净的领导者选举,在这种情况下你的持久性会降低。
更新 - 领导者选举和首选副本:
组成集群的所有节点都已经在 zK 中注册。当其中一个节点发生故障时,zK 会通知控制器节点(它本身是由 zK 选举产生的)。当这种情况发生时,其中一个实时 ISR 将被选为新的领导者。但是 Kafka 有“首选副本”的概念来平衡集群节点之间的领导分布。这是使用 启用的auto.leader.rebalance.enable=true
,在这种情况下,控制器将尝试将领导权移交给该首选副本。此首选副本是 ISR 列表中的第一个代理。这有点复杂,但只有 Kafka 管理员需要知道这一点。