Kafka 的 KRaft 模式

这不仅是架构升级,更是一场 "去 ZooKeeper 化"的独立宣言!

"以前 Kafka 是个富二代,事事靠 ZooKeeper 这个管家;现在它自己当 CEO 了------账本、选举、心跳,全自己管!"虽然事情多一点,像赵匡胤一样来一出杯酒释兵权,什么都是自己 稳妥。

现象:ZooKeeper 成了瓶颈、单点、运维噩梦

  • 集群扩容?先改 ZK 配置;什么不仅仅要改自己,还要让zk改,这怎么好意思~
  • ZK 宕机?Kafka 全集群只读(连生产都停!);什么,一个配置连累了九族!
  • 安全认证?要配两套(Kafka + ZK);这世界也太复杂了,崩溃呀真的是崩溃吖
  • 监控告警?两个系统,日志割裂,这没必要,完全没必要,谁能来救救我、救救我

根因:ZooKeeper 是"外部依赖",不是"亲儿子"

Kafka 原本把 元数据管理(谁是 Controller?哪些 Partition 在哪?)全交给 ZooKeeper:

  • Controller 选举 → ZK
  • Broker 注册 → ZK
  • Topic 配置变更 → ZK

结果

  • 架构复杂(3 个组件:Producer + Broker + ZK)
  • 性能卡脖子(元数据操作全走 ZK 网络
  • 一致性难保障(Kafka 和 ZK 状态可能不一致)

🔍 本质问题核心控制平面外包了

cap原理不得不拓宽一下了,ai能用怎么智能吗,能想的怎么全面嘛------能 😭😭😭

CAP 原理:不是"三选二",而是"网络分区时,你必须在一致性和可用性之间做选择"

"CAP 不是菜单,不能点两个;它是火灾警报------只有起火(网络分区)时,才逼你跳窗(放弃 C 或 A) 。"在一个 分布式数据存储系统 中,当发生网络分区(Partition)时,你无法同时保证:

  • C(Consistency):所有节点看到的数据是一致的(强一致性,如线性一致性)
  • A(Availability):每个请求都能收到响应(非错误),即使部分节点宕机

关键前提P(Partition tolerance)是必须的

因为网络会断、机器会崩------任何实用的分布式系统都必须容忍分区

所以,真实含义是

"在网络分区发生时,系统只能在 C 和 A 之间二选一。"

误解 正确理解
"CAP 是三选二" ❌ P 不可选!实际是 P 发生时,C vs A 二选一
"MySQL 是 CA 系统" ❌ 单机数据库不适用 CAP(无分区);分布式 MySQL 集群仍要面对 P
"最终一致性 = 放弃 C" ⚠️ 最终一致性是 弱一致性模型,属于 AP 系统的一种实现
🧪 举个生活化例子:银行跨行转账

假设你从 北京工行 → 上海建行 转账 1 万元。

场景:京沪光缆被挖断(网络分区)
  • 选 C(强一致)

    → 系统拒绝转账:"网络异常,请稍后再试"

    数据绝对不错 ,但 服务不可用(A 丢了)

  • 选 A(高可用)

    → 工行扣款成功,建行说"钱在路上"

    → 你查余额:工行 -1w,建行 +0

    服务可用 ,但 数据暂时不一致(C 丢了)

    → 后续靠对账补偿(最终一致)

1、现实中,银行选 C(宁可停机,不能错账) 2、互联网 App 选 A(先发出去,错了再退)

系统 CAP 选择 原因
etcd / ZooKeeper CP 用作协调服务,一致性 > 可用性(宁可不可用,不能给错锁)
Cassandra / DynamoDB AP 高可用优先,允许短暂不一致(购物车少一件?刷新就好)
Kafka(ZK 模式) CP(元数据) Controller 选举依赖 ZK,分区时暂停生产
Kafka(KRaft 模式) CP(元数据) Raft 协议本质是 CP
MongoDB(主从) CP(默认) 主节点挂了,从节点不自动升主(避免脑裂)
Redis Cluster AP 分区时各分片继续服务,可能写冲突

⚠️ 注意:同一系统不同组件可能不同选择

例如 Kafka:数据平面(消息写入)是 AP控制平面(元数据)是 CP

那"最终一致性"算什么
  • 最终一致性 ≠ 放弃一致性 ,而是 延迟满足的一致性
  • 它属于 AP 系统的典型策略
    • 先保证可用(A)
    • 通过 异步复制、冲突解决(如 vector clock)、补偿事务 达到最终一致
  • 用户感知:操作可能"暂时看不到结果",但"最终会正确"

💡 最终一致性是工程智慧:用时间换可用性,用复杂度换体验。

解决:KRaft(Kafka Raft Metadata mode)------ 自研"元数据引擎"

KRaft = Kafka + Raft + Metadata = 真·分布式原生

核心思想-------分二步
  • 把元数据存储从 ZooKeeper 迁移到 Kafka 自己的内部 topic(__cluster_metadata
  • Raft 协议 实现 Controller 选举与日志复制。
📦 架构对比
功能 ZooKeeper 模式 KRaft 模式
Controller 选举 ZK 临时节点 Raft Leader 选举
元数据存储 ZK znode Kafka 内部 topic
Broker 发现 ZK /brokers/ids 直接连 Controller
扩容 改 ZK + Broker 只改 Broker
安全 SASL/SSL ×2 SASL/SSL ×1

✅ so 现在 Kafka 集群 = 只有 Kafka

🚀 KRaft 的三大"成人技能"

1️⃣ Controller Quorum(控制器法定人数)
  • 一组专用节点(可复用 Broker)运行 Metadata Controller
  • 使用 Raft 协议 达成一致(比 ZK 的 ZAB 更轻量)
  • 不再依赖外部协调服务
2️⃣ Self-Contained 集群
复制代码
# 启动一个纯 KRaft 集群(无 ZK!)
kafka-storage.sh format -t $(kafka-storage.sh random-uuid) -c config/kraft/server.properties
kafka-server-start.sh config/kraft/server.properties

🎉 一条命令起集群,ZooKeeper?谁?咱们三不原则:没见过、不认识、不了解。

3️⃣ 线性扩展元数据吞吐
  • ZK 模式:元数据操作 ≈ 1k/s(受 ZK I/O 限制)
  • KRaft 模式:10w+/s(用 Kafka 自身高吞吐写日志)

💡 Kafka 用自己最擅长的事(高吞吐日志)解决自己的痛点 ------ 这叫"自反性架构"。

所以时机已到,Kafka不得不 对 ZooKeeper 说:**'谢谢你陪我长大,但现在我要自己记账了。'**什么?说我们忘恩负义?这叫青出于蓝,污蔑、赤裸裸的污蔑!

  • 于是它把账本(metadata)写进自己的日记本(__cluster_metadata),
  • 选了个班长(Raft Leader)负责盖章,
  • 其他同学(Brokers)每天抄作业(fetch metadata)。

从此,Kafka 不再是'分布式消息队列',而是'自带操作系统的消息内核'。 " 🦁 KRaft 不只是技术升级,更是 Kafka 的"精神独立"。 从此,消息、存储、控制,三位一体------这才是真正的流平台内核。

⚠️ 注意:KRaft 不是万能药

限制 说明
仅 Kafka 3.3+(2022.10 后) 旧版本必须用 ZK
滚动升级复杂 从 ZK → KRaft 需双写迁移(官方提供工具)
Controller 节点需独立规划 高负载集群建议分离 Controller 和 Data 角色

但长远看:KRaft 是 Kafka 的必然归宿。就像人总要离开父母,系统也该摆脱外部依赖

终极 Checklist(是否该上 KRaft?)

  • 新建集群?→ 直接 KRaft!
  • 老集群?→ 规划 ZK → KRaft 迁移 (用 kafka-metadata.sh
  • 运维简化?→ 告别 ZK 监控、ZK 调优、ZK 安全
  • 追求极致性能?→ 元数据操作提速 10--100 倍

📦 附件 1:KRaft 新集群一键部署脚本(Shell)

✅ 适用于 Kafka 3.3+ (官方从 3.3 开始 GA,3.5+ 推荐生产使用)

✅ 自动格式化存储、生成 UUID、启动纯 KRaft 模式

无 ZooKeeper 依赖

复制代码
kraft-deploy/
├── start-kraft.sh
├── server.properties
└── README.md
server.properties
复制代码
# 全局唯一集群 ID(由脚本自动生成)
process.roles=broker,controller
node.id=1

# Controller 监听(元数据管理)
controller.listener.names=CONTROLLER
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
advertised.listeners=PLAINTEXT://localhost:9092

# Controller Quorum 配置(本例单节点,生产需 3 节点)
controller.quorum.voters=1@localhost:9093

# 存储路径
log.dirs=/tmp/kraft-combined-logs

🔍 关键参数说明

  • process.roles=broker,controller:该节点同时承担数据和控制角色(开发/测试用)
  • 生产环境建议分离 :专用 controller 节点(仅 process.roles=controller
📜 start-kraft.sh
复制代码
#!/bin/bash
set -e

KAFKA_HOME=${KAFKA_HOME:-"/opt/kafka"}
CLUSTER_ID=""
CONFIG="server.properties"
LOG_DIR="/tmp/kraft-combined-logs"

# 1. 生成集群 ID(首次运行)
if [ ! -f ".cluster_id" ]; then
    echo "生成新集群 ID..."
    CLUSTER_ID=$($KAFKA_HOME/bin/kafka-storage.sh random-uuid)
    echo "$CLUSTER_ID" > .cluster_id
else
    CLUSTER_ID=$(cat .cluster_id)
fi

echo "集群 ID: $CLUSTER_ID"

# 2. 格式化存储目录(幂等操作)
echo "格式化存储目录..."
$KAFKA_HOME/bin/kafka-storage.sh format \
  -t "$CLUSTER_ID" \
  -c "$CONFIG" \
  --ignore-formatted

# 3. 启动 Kafka(前台运行,便于调试)
echo "启动 KRaft Broker..."
exec $KAFKA_HOME/bin/kafka-server-start.sh "$CONFIG"
使用流程(实测 Kafka 3.7)
复制代码
# 1. 下载 Kafka 3.7+
wget https://archive.apache.org/dist/kafka/3.7.0/kafka_2.13-3.7.0.tgz
tar -xzf kafka_2.13-3.7.0.tgz
export KAFKA_HOME=$(pwd)/kafka_2.13-3.7.0

# 2. 运行脚本
chmod +x start-kraft.sh
./start-kraft.sh
验证 KRaft 已启用
复制代码
# 查看日志(应出现 "Started KRaft controller")
grep "KRaft" /tmp/kraft-combined-logs/server.log

# 创建 Topic(无需 ZK!)
$KAFKA_HOME/bin/kafka-topics.sh --create \
  --topic test \
  --partitions 3 \
  --replication-factor 1 \
  --bootstrap-server localhost:9092

# 列出 Topic
$KAFKA_HOME/bin/kafka-topics.sh --list --bootstrap-server localhost:9092

成功标志 :命令执行无报错,且不提示 --zookeeper 参数缺失

附件 2:ZooKeeper → KRaft 迁移 Checklist(生产级)

⚠️ 重要前提

  • 仅 Kafka 3.5.0+ 支持双写迁移(3.3--3.4 为实验性)
  • 必须先升级到 Kafka 3.5+(仍用 ZK 模式),再迁移
🔁 迁移四步法(官方推荐)
步骤 操作 验证命令
1. 升级 Kafka 到 3.5+ 所有 Broker 升级,仍用 ZK 模式 kafka-broker-api-versions.sh --bootstrap-server ...
2. 启用双写(Dual Write) 设置 migrate.zookeeper.to.kraft=true kafka-configs.sh --describe --entity-type brokers --entity-default
3. 导出 ZK 元数据 → KRaft kafka-metadata.sh dump + kafka-metadata.sh load 检查 __cluster_metadata topic 是否有数据
4. 切换至纯 KRaft 停所有 Broker → 移除 ZK 配置 → 以 KRaft 模式启动 kafka-server-start.sh config/kraft/server.properties
📜 关键配置示例(双写阶段)

server.properties 中添加:

复制代码
# 启用双写(Kafka 3.5+)
migrate.zookeeper.to.kraft=true

# 指向 KRaft controller quorum(新配置)
controller.listener.names=CONTROLLER
controller.quorum.voters=1@ctrl1:9093,2@ctrl2:9093,3@ctrl3:9093

⚠️ 注意

  • 双写期间,所有元数据变更会同时写 ZK 和 KRaft
  • 不可跳过此阶段!否则元数据丢失
🔄 回滚预案(迁移失败怎么办?)
  1. 停止所有 Broker
  2. 从备份恢复 ZK 数据(迁移前务必备份!)
  3. 移除 KRaft 配置 ,恢复原 zookeeper.connect=...
  4. 以 ZK 模式重启

备份命令(迁移前执行) zkCli.sh dump /brokers /topics /config > zk-backup-$(date +%F).txt

版本兼容性表(官方)
Kafka 版本 KRaft 状态 生产可用? 备注
3.3.x GA(首次正式发布) ⚠️ 谨慎 缺少工具,不推荐
3.4.x 改进 ⚠️ 谨慎 仍缺迁移工具
3.5.x ✅ 完整支持 ✔️ 推荐 首个支持 ZK→KRaft 迁移
3.6.x+ 默认模式 ✔️ 强烈推荐 官方逐步弃用 ZK

总结:行动建议

  • 新集群直接 KRaft(用附件 1 脚本)
  • 老集群升级到 3.5+ → 按附件 2 迁移
  • 永远不要 在 Kafka 3.5+ 新集群中引入 ZooKeeper
相关推荐
only-qi2 小时前
RedLock:分布式锁的设计争议与实战踩坑
分布式
yangyanping201082 小时前
消息队列之消费者如何获取消息
分布式·架构·kafka
AlickLbc3 小时前
RabbitMQ安装记录
分布式·rabbitmq
星辰_mya3 小时前
Kafka Producer 发送慢 → TPS 骤降 90%
java·数据库·kafka
切糕师学AI3 小时前
Apache ZooKeeper 简介
分布式·zookeeper·apache
lucky67074 小时前
Laravel7.X十大核心特性解析
spring boot·kafka·linq
Francek Chen4 小时前
【大数据存储与管理】分布式文件系统HDFS:05 HDFS存储原理
大数据·hadoop·分布式·hdfs
星辰_mya4 小时前
Kafka之Broker 磁盘写满 → 整个集群只读
分布式·kafka
星辰_mya4 小时前
Kafka Consumer Group Rebalance 频繁
分布式·kafka