4 种 Kafka 网络中断和网络分区场景分析
本文分享自华为云社区《Kafka网络中断和网络分区场景分析》,作者: 中间件小哥。
以 Kafka 2.7.1 版本为例,依赖 zk 方式部署
3 个 broker 分布在 3 个 az,3 个 zk(和 broker 合部),单分区 3 副本
1. 单个 broker 节点和 leader 节点网络中断
网络中断前:
broker-1 和 broker-0(leader)间的网络中断后,单边中断,zk 可用(zk-1 为 leader,zk-0 和 zk-2 为 follower,zk-0 会不可用,但 zk 集群可用,过程中可能会引起原本连在 zk-0 上的 broker 节点会先和 zk 断开,再重新连接其他 zk 节点,进而引起 controller 切换、leader 选举等,此次分析暂不考虑这种情况),leader、isr、controller 都不变
az2 内的客户端无法生产消费(metadata 指明 leader 为 broker-0,而 az2 连不上 broker-0),az1/3 内的客户端可以生产消费,若 acks=-1,retries=1,则生产消息会失败,error_code=7(REQUEST_TIMED_OUT)(因为 broker-1 在 isr 中,但无法同步数据),且会发两次(因为 retries=1),broker-0 和 broker-2 中会各有两条重复的消息,而 broker-1 中没有;由于 broker-0 没有同步数据,因此会从 isr 中被剔除,controller 同步 metadata 和 leaderAndIsr,isr 更新为[2,0]
网络恢复后,数据同步,更新 isr
2. 单个 broker 节点和 controller 节点网络中断
broker 和 controller 断连,不影响生产消费,也不会出现数据不一致的情况
而当发生 leader 和 isr 变化时,controller 无法将 leader 和 isr 的变化更新给 broker,导致元数据不一致
broker-0 故障时,controller(broker-2)感知,并根据 replicas 选举新的 leader 为 broker-1,但因为和 broker-1 网络中断,无法同步给 broker-1,broker-1 缓存的 leader 依然是 broker-0,isr 为[1,2,0];当客户端进行生产消费时,如果从 broker-2 拿到 metadata,认为 leader 为 1,访问 broker-1 会返回 NOT_LEADER_OR_FOLLOWER;如果从 broker-1 拿到 metadata,认为 leader 为 0,访问 broker-0 失败,都会导致生产消费失败
3. 非 controller 节点所在 az 被隔离(分区)
zk-0 和 zk-1、zk-2 不通,少于半数,az1 内 zk 不可用,broker-0 无法访问 zk,不会发生控制器控制器选举,controller 还是在 broker-1
网络恢复后,broker-0 加入集群,并同步数据
3.1 三副本分区(replicas:[1,0,2]),原 leader 在 broker-1(或 broker-2)
az1 内:
broker-0 无法访问 zk,感知不到节点变化,metadata 不更新(leader:1,isr:[1,0,2]),依然认为自己是 follower,leader 在 1;az1 内的客户端无法生产消费
az2/3 内:
zk 可用,感知到 broker-0 下线,元数据更新,且不发生 leader 切换(isr:[1,0,2] -> [1,2],leader:1);az2 和 az3 内的客户端可正常生产消费
3.2 三副本 partition(replicas:[0,1,2]),原 leader 在 broker-0
az1 内:
zk-0 和 zk-1、zk-2 连接中断,少于一半,az1 内 zk 集群不可用,Broker-0 连不上 zk,无法感知节点变化,且无法更新 isr,metadata 不变,leader 和 isr 都不变;az1 内客户端可以继续向 broker-0 生产消费
az2/3 内:
zk-1 和 zk-2 连通,zk 可用,集群感知到 broker-0 下线,触发 leader 切换,broker-1 成为新的 leader(时间取决于 zookeeper.session.timeout.ms),并更新 isr;az2/3 内的客户端可以向 broker-1 生产消费
此时,该分区出现了双主现象,replica-0 和 replica-1 均为 leader,均可以进行生产消费
若两个隔离域内的客户端都生产了消息,就会出现数据不一致的情况
示例:(假设网络隔离前有两条消息,leaderEpoch=0)
网络隔离前:
az1 隔离后,分区双主,az1 内的客户端写入 3 条消息:c、d、e,az2/3 内的客户端写入 2 条消息:f、g:
这里 leaderEpoch 增加 2,是因为有两次增加 leaderEpoch 的操作:一次是 PartitionStateMachine 的 handleStateChanges to OnlinePartition 时的 leader 选举,一次是 ReplicationStateMachine 的 handleStateChanges to OfflineReplica 时的 removeReplicasFromIsr
网络恢复后:
由于 controller 在 broker-2,缓存和 zk 中的 leader 都是 broker-1,controller 会告知 broker-0 makerFollower,broker-0 随即 add fetcher,会先从 leader(broker-1)获取 leaderEpoch 对应的 endOffset(通过 OFFSET_FOR_LEADER_EPOCH),根据返回的结果进行 truncate,然后开始 FETCH 消息,并根据消息中的 leaderEpoch 进行 assign,以此和 leader 保持一致
待数据同步后,加入 isr,并更新 isr 为[1,2,0]。之后在触发 preferredLeaderElection 时,broker-0 再次成为 leader,并增加 leaderEpoch 为 3
在网络隔离时,若 az1 内的客户端 acks=-1,retries=3,会发现生产消息失败,而数据目录中有消息,且为生产消息数的 4 倍(每条消息重复 4 次)
有前面所述可知,网络恢复后,offset2-13 的消息会被覆盖,但因为这些消息在生产时,acks=-1,给客户端返回的是生产失败的,因此也不算消息丢失
因此,考虑此种情况,建议客户端 acks=-1
4. Controller 节点所在 az 被隔离(分区)
4.1 Leader 节点未被隔离
网络中断后,az3 的 zk 不可用,broker-2(原 controller)从 zk 集群断开,broker-0 和 broker-1 重新竞选 controller
最终 broker-0 选举为 controller,而 broker-2 也认为自己是 controller,出现 controller 双主,同时因连不上 zk,metadata 无法更新,az3 内的客户端无法生产消费,az1/2 内的客户端可以正常生产消费
故障恢复后,broker-2 感知到 zk 连接状态发生变化,会先 resign,再尝试竞选 controller,发现 broker-0 已经是 controller 了,放弃竞选 controller,同时,broker-0 会感知到 broker-2 上线,会同步 LeaderAndIsr 和 metadata 到 broker-2,并在 broker-2 同步数据后加入 isr
4.2 Leader 节点和 controller 为同一节点,一起被隔离
隔离前,controller 和 leader 都在 broker-0:
隔离后,az1 网络隔离,zk 不可用,broker-2 竞选为 controller,出现 controller 双主,同时 replica-2 成为 leader,分区也出现双主
此时的场景和 3.2 类似,此时生产消息,可能出现数据不一致
网络恢复后的情况,也和 3.2 类似,broker-2 为 controller 和 leader,broker-0 根据 leaderEpoch 进行 truncate,从 broker-2 同步数据
加入 isr,然后通过 preferredLeaderElection 再次成为 leader,leaderEpoch 加 1
5. 补充:故障场景引起数据不一致
5.1 数据同步瞬间故障
初始时,broker-0 为 leader,broker-1 为 follower,各有两条消息 a、b:
leader 写入一条消息 c,还没来得及同步到 follower,两个 broker 都故障了(如下电):
之后 broker-1 先启动,成为 leader(0 和 1 都在 isr 中,无论 unclean.leader.election.enable 是否为 true,都能升主),并递增 leaderEpoch:
然后 broker-0 启动,此时为 follower,通过 OFFSET_FOR_LEADER_EPOCH 从 broker-1 获取 leaderEpoch=0 的 endOffset
broker-0 根据 leader epoch endOffset 进行 truncate:
之后正常生产消息和副本同步:
该过程,如果 acks=-1,则生产消息 c 时,返回客户端的是生产失败,不算消息丢失;如果 acks=0 或 1,则消息 c 丢失
5.2 unclean.leader.election.enable=true 引起的数据丢失
还是这个例子,broker-0 为 leader,broker-1 为 follower,各有两条消息 a、b,此时 broker-1 宕机,isr=[0]
在 broker-1 故障期间,生产消息 c,因为 broker-1 已经不在 isr 中了,所以即使 acks=-1,也能生产成功
然后 broker-0 也宕机,leader=-1,isr=[0]
此时 broker-1 先拉起,若 unclean.leader.election.enable=true,那么即使 broker-1 不在 isr 中,因为 broker-1 是唯一活着的节点,因此 broker-1 会选举为 leader,并更新 leaderEpoch 为 2
这时,broker-0 再拉起,会先通过 OFFSET_FOR_LEADER_EPOCH,从 broker-1 获取 epoch 信息,并进行数据截断
再进行生产消息和副本同步
消息 c 丢失
版权声明: 本文为 InfoQ 作者【华为云开发者联盟】的原创文章。
原文链接:【http://xie.infoq.cn/article/d51e580b7a211e7146f7ca35b】。文章转载请联系作者。
评论