这是《Kafka 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 KRaft(Kafka Raft)是 Kafka 4.x 唯一的运行模式。本章深入它的动机、架构、元数据日志与运维要点,并说明如何从 ZooKeeper 集群迁移。
16.1 为什么要去掉 ZooKeeper
ZooKeeper 模式下的痛点:
- 两套系统:Broker 依赖 ZooKeeper 选举 Controller、存元数据;运维要同时维护两个分布式系统的安全、扩容、监控;
- 元数据双写:Controller 变更先写 ZooKeeper,再推送给 Broker,路径长、窗口内有不一致;
- 扩展瓶颈:分区数达到十万级时,Controller 故障切换要重放大量状态,收敛可能需要分钟级;
- 安全与网络复杂:客户端、Broker、ZooKeeper 三方网络与认证都要打通。
KRaft 的思路:既然 Kafka 本身就是高吞吐、高可用的分布式日志,那就用 Kafka 存 Kafka 的元数据。
16.2 架构与角色
┌──────────────────────────────────┐
│ __cluster_metadata (Raft log) │
│ topic 配置 / broker 注册 / 分区 │
│ Leader 选举 / ACL / 配额 ... │
└───────────────┬──────────────────┘
│ 复制
┌─────────────┬───────┴──────┬─────────────┐
v v v v
controller1 controller2 controller3 broker...
(voter) (voter) (voter) (learner)
角色由 process.roles 定义:
| 取值 | 含义 | 场景 |
|---|---|---|
controller |
只参与仲裁,不存业务数据 | 大规模分离部署 |
broker |
只存数据,作为 learner 同步元数据 | 分离部署的数据节点 |
broker,controller |
兼具两种角色 | 中小集群合并部署 |
核心概念:
- Voter:有投票权的 Controller(配置在
controller.quorum.voters),数量为奇数; - Learner:Broker,只拉取元数据日志,不投票;
- 元数据日志:一个特殊的内部 topic
__cluster_metadata,提交即生效; - Snapshot:元数据日志定期快照,新节点/落后节点先加载快照再追增量。
16.3 元数据变更如何发生
以“创建 topic”为例:
1. 客户端向任意 Broker 发 CreateTopics 请求
2. Broker 转发给 Leader Controller
3. Leader 把变更作为记录追加到 __cluster_metadata
4. Raft 多数派确认 -> commit
5. 所有 Broker 的元数据日志推进到该 offset
6. 客户端收到创建成功
对比 ZooKeeper 模式:不再存在“先写 ZK 再通知”的两阶段窗口,元数据的真相只有一份日志,所有节点按同一顺序回放。这从机制上消除了元数据分叉。
16.4 配置速览
# 节点角色与 ID
process.roles=broker,controller
node.id=1
# 仲裁者列表:id@host:controller端口
controller.quorum.voters=1@broker1:9093,2@broker2:9093,3@broker3:9093
# 监听器
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
advertised.listeners=PLAINTEXT://broker1:9092
controller.listener.names=CONTROLLER
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
# 数据目录
log.dirs=/data/kafka
运维要点:
controller.quorum.voters集群生命周期内基本不变;扩 Controller 是“加 voter + 滚动重配”的严肃操作,规划时直接定 3 或 5;controller.listener.names指向的监听器专用于仲裁,不要暴露给客户端;生产环境建议对 Controller 通道启用 SSL 或 SASL;node.id全集群唯一,且与controller.quorum.voters中的 id 严格一致。
16.5 常用运维命令
# 仲裁状态:谁是 Leader、HW、voter 列表
bin/kafka-metadata-quorum.sh \
--bootstrap-server localhost:9092 describe --status
# 每个 voter 的同步详情
bin/kafka-metadata-quorum.sh \
--bootstrap-server localhost:9092 describe --replication
# 查看元数据日志(排障)
bin/kafka-dump-log.sh \
--files /data/kafka/__cluster_metadata-0/00000000000000000000.log \
--cluster-metadata-decoder
describe --replication 里的 LastFetchTimestamp、LastCaughtUpTimestamp 能判断哪些 voter 落后。
16.6 故障场景
| 场景 | 影响 |
|---|---|
| Leader Controller 宕机 | 多数派重新选出 Leader,期间元数据变更短暂不可用,数据读写基本不受影响 |
| 一个 Follower Controller 宕机 | 3 节点仲裁仍有多数派,无感知 |
| 两个 Controller 宕机(3 节点) | 丢失多数派,元数据变更不可用;已有分区读写通常继续 |
| Broker learner 落后 | 该 Broker 可能用旧元数据应答,客户端重试后纠正;持续落后需查网络/磁盘 |
和 Kafka 数据面一样:元数据面也是少数派服从多数派。3 节点 Controller 可以容忍 1 台故障,5 节点容忍 2 台。
16.7 从 ZooKeeper 迁移到 KRaft
Kafka 3.4-3.9 提供在线迁移工具,思路是“双写过渡”:
阶段1 ZK 模式:
Controller <- ZooKeeper
阶段2 迁移中:
ZK 里的元数据被迁移工具搬运到 __cluster_metadata
新 KRaft Controller 作为 DualWriter 同时服务
Broker 逐台切换为 KRaft 配置
阶段3 KRaft 模式:
ZooKeeper 不再被依赖,可下线
操作要点:
- 先升级到支持迁移的 3.x 版本,做全量备份;
- 搭建 KRaft Controller 仲裁(通常 3 节点);
- 按 runbook 执行格式化与迁移准备;
- 触发
ZkMigrationState切换,观察元数据一致性; - 滚动重启 Broker 切到 KRaft 配置;
- 观察
kafka-metadata-quorum.sh正常后,摘除 ZooKeeper。
迁移是高风险变更,务必在预发演练并准备好回退窗口。Kafka 4.0 已不支持 ZK 模式,老集群终将走上这条路。
16.8 监控要点
关键 JMX 指标:
kafka.controller:type=KafkaRaftServer/raft-metrics:current-state、leader-id、commit-offset、high-watermark;ActiveControllerCount:应为 1(KRaft 下表示有唯一 Leader);MetadataFencedCount、MetadataLoadErrorCount:出现非零要立刻排查;- Follower voter 的
LastCaughtUpTimestamp:仲裁健康度。
告警建议:Controller Leader 长期缺失、voter 长期未追上、Broker 元数据 offset 滞后 Leader 过大。
本章小结
- KRaft 用 Raft 日志
__cluster_metadata取代 ZooKeeper,元数据单一路径、按序回放; process.roles决定节点角色,voter 是奇数的 Controller,broker 是 learner;- 3 Controller 容忍 1 故障,5 容忍 2;Controller 通道应独立且加密;
- 快照让新节点快速加载状态,
kafka-metadata-quorum.sh是核心巡检命令; - ZK -> KRaft 迁移是双写过渡的严肃变更,需演练与回滚预案。
思考题
- 为什么 Broker 宕机不影响 KRaft 仲裁?为什么 Controller 宕机一般也不影响数据读写?
- 元数据日志和普通 topic 日志有什么相同与不同?
- 如果公司还在 ZK 模式,你会如何制定迁移评估清单?