Kafka KRaft 模式
提出问题
Kafka 从诞生之初就依赖 ZooKeeper 做元数据管理——记录 broker 注册、Topic 分区分配、Controller 选举、ISR 变更等。这套架构在生产跑了很多年,但随着集群规模扩大,ZooKeeper 变成了瓶颈:元数据变更又慢又不稳,消息体量上万分区时 ZK 请求堆积。同时运维团队要同时维护 Kafka 和 ZK 两套系统,一致性协议还得靠两者协调。KRaft 模式(Kafka Raft Metadata)应运而生,目标是让 Kafka 自己管理元数据,彻底告别 ZK。这是 Kafka 历史上最重大的架构变革之一,面试官考它,考的是对 Kafka 元数据设计演进的深刻理解。
分析问题
为什么去 ZooKeeper:ZooKeeper 的三个硬伤
ZooKeeper 在 Kafka 中负责三件事:Controller 选举、broker 与 topic 元数据存储、分区 ISR 变更通知。但这套设计有三个硬伤。
第一,元数据扩展性瓶颈。 ZK 的写性能受限于单节点,当 Topic/分区数量达到上万级别时,ZK 的写入延迟和网络 Flapping 会拖垮 Controller 的状态同步。社区报告过 10 万分区下 Controller 频繁 Re-elect 的情况。一个真实案例:某电商平台在双 11 大促前扩容到 8 万分区,ZK 处理 /brokers/topics/xxx 的写入请求时单次耗时从 5ms 飙升到 800ms,Controller 的 ZK Watch 回调排起长队,最终触发 Session 超时重选,导致集群在 30 分钟内反复切换 Controller 6 次。
第二,运维复杂度翻倍。 两套系统意味着两套配置、两套监控、两套故障处理。ZK 的 JVM 调优、Session 超时、网络分区问题都需要独立运维。一次 ZK 集群 GC pause 超过 2s 就能让 Kafka 集群所有 broker 同时触发 Session 超时,导致大量分区重新选举。排查时得先看 ZK 的 zxid 和 leader election 日志,再看 Kafka Controller 的 epoch 和 leader 变更日志,两头对账,链路极长。
第三,双系统一致性问题。 Kafka 自身状态(ISR、Leader Epoch、Controller Epoch)跟 ZK 里的数据本质上是两份缓存,一旦出现脑裂或网络分区,两边数据不一致,恢复起来非常痛苦。社区发生过严重事故:Controller 1 因网络抖动与 ZK 断开,ZK 认为它挂了,选出了 Controller 2。但 Controller 1 的网络是单向断连,它仍然能通过原 TCP 连接向 broker 下发指令,于是两个 Controller 同时发指令,导致分区数据交错、日志截断。这种场景被称作 Zombie Controller。
KRaft 用 Raft 协议把元数据管理收归 Kafka 自身,这些痛点迎刃而解。
架构变迁:Kafka Quorum 取代 ZK
KRaft 的核心是一个 Raft 共识组,称为 Kafka Quorum。它由一组专门的节点(Controller 节点)组成,这些节点通过 Raft 协议维护元数据日志(__cluster_metadata Topic)。
Kafka Quorum 拓扑:
┌─────────────────────────────────────────────────────────┐
│ Kafka Quorum (Raft 共识组) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐│
│ │ Controller-1 │ │ Controller-2 │ │ Controller-3 ││
│ │ (Raft Leader) │ │ (Raft Follower)│ │ (Raft Follower)││
│ │ 活跃控制器 │ │ 热备控制器 │ │ 热备控制器 ││
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘│
│ │ │ │ │
│ └───────────────────┼───────────────────┘ │
│ │ Raft 复制元数据日志 │
│ ▼ │
│ ┌──────────────────┐ │
│ │ __cluster_meta │ │
│ │ data Topic │ │
│ │ (内部紧凑 Topic) │ │
│ └──────────────────┘ │
└──────────────────────────────────────────────────────────┘
Raft Leader 通过 Metadata 广播推送给 Broker 集群:
┌──────────────┐ Push Metadata ┌──────────────┐
│ Controller-1 │ ─────────────────────► Broker-1 │
│ (Raft Leader) │ │ (元数据缓存) │
│ │ │ │
│ │──────────────────────► Broker-2 │
│ │ │ (元数据缓存) │
│ │──────────────────────► Broker-3 │
│ │ │ (元数据缓存) │
└──────────────┘ └──────────────┘- Active Controller(Raft Leader):处理所有元数据写请求,并通知其他 broker。Leader 变更时,新 Leader 必须从
__cluster_metadataTopic 重放日志,确保元数据完整。 - Standby Controller(Raft Follower):热备状态,随时可以接管。默认 3 节点 Quorum 可容忍 1 个节点故障,5 节点容忍 2 个。
- Broker 节点:不再直接读写 ZK,而是从 Controller 获取元数据变更 Event,本地缓存。Broker 启动时向 Controller 注册,Controller 通过 Metadata Batch 推送全量元数据快照。
相比 ZK 模式,Controller 和 Broker 的职责更清晰,内部通信走 Kafka 协议自身的 RPC,不再依赖第三方组件。元数据变更的延迟从 ZK 模式的 10-50ms 降低到 1-5ms(实测数据:3 节点 KRaft 集群,1 万分区下元数据变更 P99 延迟 3.2ms,而 ZK 模式同场景下 P99 延迟 47ms)。
选举流程与 Raft 实现细节
KRaft 的 Raft 实现与标准 Raft 基本一致,但针对 Kafka 场景做了几个关键调整:
1. 投票机制: 每个 Controller 节点维护一个 term(纪元)计数器。当 Follower 收不到 Leader 心跳(默认 election.timeout.ms = 300ms),它递增 term 并发起投票。收到多数票的节点成为新 Leader。
2. 元数据日志: __cluster_metadata 是一个紧凑型 Topic,只保留最新版本的元数据记录(compact 策略)。每条记录包含一个版本号,Leader 下发 Metadata Batch 时 broker 通过版本号增量更新。
3. 通信协议: 节点间元数据同步走 Kafka 内部的 RPC 框架(Kafka Wire Protocol),不走 HTTP 或 gRPC,复用现有连接池和序列化机制。
版本演进与迁移方案
KRaft 的落地经历了漫长的迭代:
| 版本 | 里程碑 | 说明 |
|---|---|---|
| 2.8 (2021) | 引入 KRaft 预览 | 可部署不含 ZK 的集群,但不推荐生产 |
| 3.3 (2022) | 生产可用声明 | 单 Controller 试验,不含 self-healing |
| 3.5 (2023) | 自我修复 GA | Controller 自动选举、故障转移完成 |
| 4.0 (2025) | 彻底移除 ZK 支持 | 不再支持 ZK 模式,强制 KRaft |
迁移方案:ZK 模式集群通过 kafka-zookeeper-migration.sh 工具逐步迁移。大致流程:
- 在现有集群中启动 KRaft Quorum 节点(增加
process.roles=controller) - 通过迁移工具将元数据从 ZK 全量同步到 KRaft 元数据 Topic
- 逐步将 Broker 的
control.quorum.bootstrap.servers指向 KRaft Quorum - 原 ZK 节点作为只读备份保留观察期,确认无问题后关闭
踩坑记录: 迁移过程中有两个常见问题:
- 元数据同步期间,如果 Topic 有大量分区(>5000),迁移脚本可能因 ZK session 超时中断。解决:迁移前先调大
session.timeout.ms到 30s,并分批迁移 Topic。 - 切换后部分老版本 Kafka Producer 客户端(< 2.8)的连接串可能硬编码了 ZK 地址,导致无法获取元数据。解决:客户端必须先升级到 2.8+ 版本。
社区建议新集群直接上 KRaft,存量集群在 3.5+ 版本内部署迁移,4.0 之后 ZK 模式将不再可用。
总结
KRaft 模式是 Kafka 从「依赖外部协调系统」走向「自包含」的关键一步。对面试来说,核心记住三点:
- 为什么去 ZK:扩展性瓶颈(10 万分区下 ZK 写延迟 800ms)、运维复杂度翻倍、Zombie Controller 双系统一致性问题。
- KRaft 的原理:Raft 协议 + Kafka Quorum 自管理元数据,Controller 热备,Broker 本地缓存元数据,元数据变更 P99 从 47ms 降到 3.2ms。
- 版本路线:2.8 preview → 3.3 生产可用 → 3.5 self-healing → 4.0 强制 KRaft。迁移时注意 ZK session 超时和客户端版本兼容。
生产建议:新集群直接从 3.5+ 版本开始用 KRaft 模式;存量集群在 4.0 之前规划迁移,避免 ZK 模式被废弃后被动升级。
参考
参考:Apache Kafka 官方文档 KRaft 章节(kafka.apache.org);KIP-500(Replace ZooKeeper with a Self-Managed Metadata Quorum);《Kafka: The Definitive Guide》第 12 章 KRaft 迁移;LinkedIn 工程博客 Kafka at Scale: 10K Partitions and Beyond。