Skip to content

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_metadata Topic 重放日志,确保元数据完整。
  • 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)自我修复 GAController 自动选举、故障转移完成
4.0 (2025)彻底移除 ZK 支持不再支持 ZK 模式,强制 KRaft

迁移方案:ZK 模式集群通过 kafka-zookeeper-migration.sh 工具逐步迁移。大致流程:

  1. 在现有集群中启动 KRaft Quorum 节点(增加 process.roles=controller
  2. 通过迁移工具将元数据从 ZK 全量同步到 KRaft 元数据 Topic
  3. 逐步将 Broker 的 control.quorum.bootstrap.servers 指向 KRaft Quorum
  4. 原 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。

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。