写点什么

大数据 -74 Kafka 核心机制揭秘:副本同步、控制器选举与可靠性保障

作者:武子康
  • 2025-08-21
    山东
  • 本文字数:5048 字

    阅读完需:约 17 分钟

大数据-74 Kafka 核心机制揭秘:副本同步、控制器选举与可靠性保障

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI 篇持续更新中!(长期更新)

AI 炼丹日志-31- 千呼万唤始出来 GPT-5 发布!“快的模型 + 深度思考模型 + 实时路由”,持续打造实用 AI 工具指南!📐🤖

💻 Java 篇正式开启!(300 篇)

目前 2025 年 08 月 18 日更新到:Java-100 深入浅出 MySQL 事务隔离级别:读未提交、已提交、可重复读与串行化 MyBatis 已完结,Spring 已完结,Nginx 已完结,Tomcat 已完结,分布式服务正在更新!深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300 篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT 案例 详解


章节内容

上节我们完成了如下内容:


  • 事务相关配置

  • 事务幂等性

  • 事务操作

  • 案例 1:单 Producer 保证仅发送一次

  • 案例 2:消费-转换-生产 事务仅保证一次发送


控制器

基本介绍

Kafka 集群架构

  • Kafka 集群由若干个 Broker(服务器节点)组成,构成分布式消息系统。每个 Broker 都需要配置唯一的 broker.id 标识其身份,编号范围通常从 0 开始连续递增,确保整个集群中没有重复的 ID。例如,一个 3 节点的集群可能配置 broker.id=0、broker.id=1 和 broker.id=2。

主题与分区

  • 在 Kafka 集群上创建的主题(Topic)是消息的逻辑分类,每个主题会被划分为若干个分区(Partition)。分区是 Kafka 并行处理的基本单位,例如一个主题可以配置为 10 个分区,这样就能同时被 10 个消费者并行消费。

副本机制

  1. 每个分区都有若干个副本(Replica),副本数量由配置的副本因子(replication factor)决定。假设设置副本因子为 3,则每个分区会有 3 个副本。

  2. 副本分为两种角色:

  3. Leader 副本:负责处理该分区的所有读写请求

  4. Follower 副本:从 Leader 副本同步数据,不直接服务客户端请求

副本状态

Kafka 通过副本状态来管理数据同步:


  • ISR(In-Sync Replicas):同步副本集合,包含与 Leader 保持同步的副本。这些副本的数据与 Leader 基本一致,当 Leader 失效时,会从 ISR 中选举新的 Leader。

  • OSR(Out-of-Sync Replicas):非同步副本集合,包含与 Leader 同步滞后的副本。这些副本可能由于网络问题、机器故障等原因暂时无法与 Leader 保持同步。


示例场景:假设一个分区的副本因子为 3(1 个 Leader+2 个 Follower),其中 1 个 Follower 因为网络延迟导致同步滞后超过 replica.lag.time.max.ms(默认 30 秒)配置的时间,就会被移出 ISR 放入 OSR,直到它重新追上 Leader 的进度才会被重新加入 ISR。

Broker 选举

控制器信息

集群里第一个启动的 Broker 在 ZooKeeper 中创建了临时的节点


<KafkaZkChroot>/controller
复制代码


其他 Broker 在该控制器节点创建 ZooKeeperWatch 对象,使用 ZooKeeper 的监听机制接收该节点的变更。即:Kafka 通过 ZooKeeper 的分布式特性选举集群控制器。


下图中,节点 /controller 是一个 Zookeeper 临时节点,其中 Brokerid:0,表示当前控制器是 broker.id 为 0 的 Broker。



每个新选出的控制器通过 ZooKeeper 的条件递增操作获得一个全新的、数值更大的 controller epoch。其他 Broker 在知道当前 controller epoch 后,如果收到由控制器发出的包含较旧 epoch 的消息,就会忽略它们,以防止脑裂。比如当一个 Leader 副本分区所在的 Broker 宕机,需要选举新的 Leader 副本分区,有可能两个具有不同纪元数字的控制器都选举了新的 Leader 副本分区,如果选举出来的 Leader 副本分区不一样,听谁的?脑裂的时候,有纪元数字,直接使用纪元数字最新的控制器结果。



当控制器发现一个 Broker 离开集群,那些失去 Leader 副本分区的 Follower 分区需要一个新的 Leader。

控制器宕机

当 Kafka 集群中的控制器(Controller)发生宕机时,整个集群的协调和管理功能会受到影响。控制器是 Kafka 集群中一个特殊的 Broker 节点,负责管理集群状态、分区 Leader 选举等关键任务。控制器宕机后需要考虑以下问题:

1. 控制器宕机的影响

  • 集群协调功能中断:控制器宕机后,无法进行分区 Leader 选举、副本重分配等操作,可能导致部分分区无法正常工作

  • 元数据同步延迟:新的控制器需要重新加载集群状态信息,从 Zookeeper 获取最新数据,这期间集群状态可能出现不一致

2. 控制器恢复的步骤

控制器重新选举后,新的控制器需要进行以下操作来恢复集群状态:


  1. 重新加载元数据

  2. 从 Zookeeper 读取最新的分区和 Broker 信息

  3. 从其他 Broker 节点同步未保存的元数据

  4. 验证元数据的完整性和一致性

  5. 恢复集群状态

  6. 重新计算分区的 Leader 和 Follower 副本

  7. 检查副本的同步状态,确保数据一致性

  8. 重新分配副本,平衡集群负载

  9. 恢复集群管理功能

  10. 重新启动关键服务,如副本重分配、Leader 选举等

  11. 监控集群状态,确保恢复过程中不会出现新的问题

3. 控制器恢复的注意事项

  • 恢复时间:新控制器启动后,恢复集群状态的时间取决于集群规模和数据量

  • 数据一致性检查:在恢复过程中,需要验证数据的一致性,防止数据丢失或损坏

  • 监控和日志:在控制器恢复期间,需要密切关注集群状态和日志,及时发现和处理问题

4. 控制器宕机的预防措施

为了降低控制器宕机对集群的影响,可以采取以下预防措施:


  • 配置高可用控制器:部署多个控制器节点,避免单点故障

  • 定期备份元数据:将元数据备份到安全的位置,如分布式存储系统

  • 监控控制器状态:实时监控控制器节点的健康状况和资源使用情况,提前发现和解决问题下图中,/brokers/ids/0 保存着 Broker 的信息,此节点为临时节点,如果 Broker 节点宕机,该节点丢失。集群控制器负责监听 ids 节点,一旦节点子节点发生变化,集群控制器就会得到通知。



  • 控制器遍历这些 Follower 副本分区,并确定谁应该成为新的 Leader 分区,然后向所有包含新 Leader 分区和现有 Follower 的 Broker 发送请求。

  • 该请求消息包含了谁是新的 Leader 副本分区以及谁是 Follower 副本分区的信息,随后,新 Leader 分区开始处理来自生产者和消费者的请求,而跟随者开始从新 Leader 副本分区消费消息。

  • 当控制器发现一个 Broker 加入集群时,它会使用 BrokerId 来检查新加入的 Broker 是否包含现有分区的副本。如果有,控制器就把变更通知发送给新加入的 Broker 和其他 Broker,新 Broker 上的副本分区开始从 Leader 分区那里消费消息,与 Leader 分区保持同步。

最后结论

  • Kafka 使用 ZooKeeper 的分布式锁选举控制器,并在节点加入集群或退出集群时通知控制器

  • 控制器负责在节点加入或离开集群时进行分区 Leader 选举

  • 控制器使用 epoch 来避免脑裂(脑裂是指两个节点同时认为自己是当前的控制器)

可靠性保证

Kafka 副本机制详解

Topic 创建与副本配置

在创建 Topic 时,可以通过--replication-factor参数指定副本数量(建议设置为 3 以保证高可用性)。需要注意的是:


  1. 副本数不能超过当前集群中可用的 Broker 数量

  2. 生产环境中通常设置为 3,这样允许 1 个副本失效而不影响服务可用性

  3. 示例命令:bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic

Leader-Follower 架构

Kafka 采用主从复制架构:


  • Leader 副本

  • 每个分区只有一个 Leader

  • 负责处理所有读写请求

  • 维护 ISR(In-Sync Replicas)列表

  • Follower 副本

  • 被动复制 Leader 的数据

  • 通过定期发送 fetch 请求同步数据(默认每 500ms)

  • 不直接服务客户端请求

  • 当 Leader 失效时,Controller 会从 ISR 中选举新的 Leader

ISR 机制详解

In-Sync Replicas 是保证数据一致性的核心机制:


  1. 维护条件

  2. Follower 必须定期与 Leader 保持心跳(默认 10 秒内)

  3. 不能落后 Leader 太多(通过replica.lag.time.max.ms控制)

  4. 异常处理

  5. 当 Follower 超过replica.lag.time.max.ms(默认 10000ms)未同步时

  6. Leader 会将其从 ISR 列表中移除

  7. 监控指标:kafka.server:type=ReplicaManager,name=IsrShrinks

  8. 配置建议


   # 建议生产环境配置   replica.lag.time.max.ms=30000   unclean.leader.election.enable=false
复制代码

消息可靠性保证

通过acks参数控制消息持久化级别:


  1. acks=all(最高可靠性)

  2. 消息写入流程:

  3. Producer 发送消息到 Leader

  4. Leader 将消息写入本地日志

  5. Follower 通过 fetch 请求拉取消息

  6. Follower 持久化后发送 ACK 给 Leader

  7. Leader 收到所有 ISR 副本的 ACK 后提交消息

  8. Leader 向 Producer 返回 ACK

  9. 确保消息不会丢失,但会增加延迟(通常 50-100ms)

  10. 其他 ack 级别

  11. acks=0:不等待任何确认

  12. acks=1:仅等待 Leader 确认(默认)

  13. 生产建议:对关键业务设置acks=allmin.insync.replicas=2

最佳实践建议

  1. 监控 ISR 变化,设置告警

  2. 保持至少 3 个副本,且min.insync.replicas设置为 2

  3. 对于关键业务:


   // Producer配置示例   props.put("acks", "all");   props.put("retries", 3);   props.put("max.in.flight.requests.per.connection", 1);
复制代码


  1. 定期测试 Broker 故障场景,验证副本选举机制

副本的分配

当某个 Topic 的--replication-factor 为 N(N>1)时,每个 Partition 都有 N 个副本,称作 Replica,原则上是将 Replica 均匀地分配到整个集群上,不仅如此,Partition 的分配也同样需要均匀分配,为了更好的负载均衡。


副本分配的三个目标:


  • 均衡的将副本分撒于各个 Broker 上

  • 对于某个 Broker 上分配的分区,尽量将分区的各个副本分配到不同的机架上的 Broker。

  • 如果所有的 Broker 都有机架信息,尽量将分区的各个副本分配到不同的机架上的 Broker


在不考虑机架信息的情况下:


  • 第一个副本分区通过轮询的方式挑选一个 Broker,进行分配。该轮询从 Broker 列表的随机位置进行轮询

  • 其余副本通过增加偏移进行分配


失效的副本

失效副本判定

replica.lag.time.max.ms 默认大小为 10000 当 ISR 中的一个 Follower 副本滞后 Leader 的时间超过参数设置之后,则判断副本失效,需要将此 Follower 副本踢出 ISR。

实现原理

具体的实现原理:当 Follower 副本将 Leader 副本的 LEO 之前的日志全部同步时,则认为该 Follower 副本已经追赶上了 Leader 副本,此时更新该副本的 lastCaughtUpTimeMs 标识。Kafka 的副本管理器(ReplicaManager)启动时会启动一个副本过期检测的定时任务,而这个定时任务会定时检查当前时间与副本的 lastCaughtUpTimeMs 差值是否大于参数 replica.lag.time.max.ms 指定的值。


Kafka 的源码注释中也说了一般有两种情况会导致副本失效:


  • Follower 副本进程卡住,在一段时间内没有向 Leader 副本发起同步请求,比如频繁的 FULL GC。

  • Follower 副本进程同步过慢,在一段时间内都无法追赶上 Leader 副本,比如 IO 开销过大。


如果通过工具增加副本因子,那么新增加的副本在赶上 Leader 副本之前也都是失效状态的。如果一个 Follower 副本由于某些原因(宕机)而下线,之后又上线,在追赶上 Leader 副本之前也是处于失效状态。

如何查看

失效副本的分区个数是用于衡量 Kafka 性能指标的重要部分。Kafka 本身提供了一个相关的指标,即 UnderReplicatedPartitions,可以通过 JMX 访问:


  • kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions 取值范围是大于等于 0 的整数,需要注意,如果 Kafka 集群正在做分区迁移(kafka-reassign-partition.sh)的时候,这个值也会大于 0。

副本复制

日志复制算法(lgo replication algorithm)必须提供的基本保证是,如果它告诉客户端消息已经被提交,而当前 Leader 出现故障,新选出的 Leader 也必须具有该消息,在出现故障时,Kafka 会从挂掉 Leader 的 ISR 里面选择一个 Follower 作为这个分区新的 Leader。每个分区的 Leader 会维护一个 in-sync replica(同步副本列表,又称 ISR)。当 Producer 向 Broker 发送消息,消息先写入到对应的 Leader 分区,然后复制到这个分区的所有副本中。ACKS=ALL 时,只有将消息成功复制到所有同步副本(ISR)后,这条消息才算被提交。

什么情况会失去同步

一个副本与 Leader 失去同步的原因有很多,主要包括:


  • 慢副本(Slow Replica):Follower replica 在一段时间内一直无法赶上 Leader 的写进度,造成这种情况的最常见原因之一是 Follower replica 上的 IO 瓶颈,导致它持久化日志的时间比它从 Leader 消费时间要长

  • 卡住副本(Stuck Replica):Follower replica 在很长一段时间内停止从 Leader 获取消息,这可能是因为 GC 停顿,或者副本故障

  • 刚启动副本(Bootstrapping replica):当用户给某个主题增加副本因子时,新的 Follower replicas 是不同步的,直到它跟上

Leader 日志

当副本落后于 Leader 分区时,这个副本被认为是不同步或者滞后的,在 Kafka 中,副本的滞后于 Leader 是根据 replica.lag.tiime.max.ms 来衡量。

如何确认某个副本滞后

通过 replica.lag.time.max.ms 来检测卡住的副本(Stuck replica)在所有情况下都能很好的工作,它跟踪 Follower 副本没有向 Leader 发送获取请求的时间,通过这可以推断 Follower 是否正常。另一方面,使用消息数量检测不同步慢副本(Slow replica)的模型只有在为单个主题或者具有同类流量模式的多个主题设置这些参数时才能很好的工作,但我们发现它不能扩展到生产集群中所有主题。


发布于: 刚刚阅读数: 4
用户头像

武子康

关注

永远好奇 无限进步 2019-04-14 加入

Hi, I'm Zikang,好奇心驱动的探索者 | INTJ / INFJ 我热爱探索一切值得深究的事物。对技术、成长、效率、认知、人生有着持续的好奇心和行动力。 坚信「飞轮效应」,相信每一次微小的积累,终将带来深远的改变。

评论

发布
暂无评论
大数据-74 Kafka 核心机制揭秘:副本同步、控制器选举与可靠性保障_Java_武子康_InfoQ写作社区