Kafka + KRaft模式架构基础介绍
适用版本:Kafka 3.3+(生产推荐)
一、概述:为什么使用 KRaft?
自 Kafka 2.8 起,社区引入 KRaft(Kafka Raft Metadata)模式 ,旨在完全移除对 ZooKeeper 的依赖。从 Kafka 3.3 开始,KRaft 成为官方推荐的生产部署模式。
核心优势
- 架构简化:无需独立部署、监控、备份 ZooKeeper;
- 性能提升:元数据变更吞吐提升 5--10 倍,延迟更低;
- 高可用增强:基于 Raft 协议,选举更快、防脑裂;
- 云原生友好:StatefulSet 部署更简单,适合 Kubernetes;
- 统一运维:日志、监控、告警全部集中于 Kafka。
注意 :KRaft 仅改变元数据管理方式 ,消息存储、副本机制、ISR 等数据平面逻辑完全不变。
二、KRaft 架构模式
KRaft 支持两种部署模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| Combined Mode | 单节点同时承担 Broker + Controller 角色 | 中小集群(≤ 20 节点) |
| Dedicated Mode | Controller 与 Data Broker 物理分离 | 超大集群(> 20 节点)或高安全要求 |
三、Combined Mode 架构与工作流程
架构图
graph LR
subgraph "KRaft Controller Quorum"
C1[Controller 1
Active] C2[Controller 2
Follower] C3[Controller 3
Follower] end subgraph "Data Brokers" B1[Broker 1] B2[Broker 2] B3[Broker N] end C1 -->|Append Log| MT[__cluster_metadata] C2 -->|Replicate| MT C3 -->|Replicate| MT B1 -->|Metadata Request| C1 B2 -->|Metadata Request| C1 B3 -->|Metadata Request| C1
Active] C2[Controller 2
Follower] C3[Controller 3
Follower] end subgraph "Data Brokers" B1[Broker 1] B2[Broker 2] B3[Broker N] end C1 -->|Append Log| MT[__cluster_metadata] C2 -->|Replicate| MT C3 -->|Replicate| MT B1 -->|Metadata Request| C1 B2 -->|Metadata Request| C1 B3 -->|Metadata Request| C1
在 Combined Mode 中,
C1 = B1,C2 = B2,C3 = B3,即角色合一。
工作流程步骤
-
集群启动
- 所有节点启动 Kafka 服务;
- 每个节点根据
process.roles=broker,controller初始化双重角色; - Controller 模块尝试加入 Raft Quorum。
-
Raft 选举
- 节点通过
CONTROLLER监听器(如 9093 端口)通信; - 选举出一个 Active Controller(Leader);
- 其余节点成为 Follower,仅同步元数据日志。
- 节点通过
-
Broker 注册
- 每个 Broker 向 Active Controller 发送注册请求;
- Active Controller 将 Broker 信息写入 Metadata Log (内部 Topic
__cluster_metadata); - 日志通过 Raft 协议复制到所有 Follower。
-
元数据变更(如创建 Topic)
- 客户端请求 → 任意 Broker → 转发至 Active Controller;
- Active Controller 生成元数据变更记录,追加到 Metadata Log;
- 日志复制成功后,广播新元数据给所有 Broker。
-
消息读写(数据平面)
- Producer/Consumer 连接任意 Broker;
- 若非目标分区 Leader,Broker 返回 Redirect;
- 客户端直连 分区 Leader Broker ,不经过 Controller;
- 消息路径完全独立于控制平面。
-
故障恢复
- Active Controller 宕机 → Raft 心跳超时 → 触发新选举;
- 新 Leader 从本地 Metadata Log 恢复全量状态;
- 继续提供元数据服务,业务无感知。
关键结论:
- 控制平面:Client → Broker → Active Controller → Metadata Log(Raft 复制);
- 数据平面:Client ↔ Partition Leader Broker(直连,高效);
- Controller 不参与消息收发。
四、Raft Controller 选举流程详解
sequenceDiagram
participant C1 as Controller 1
participant C2 as Controller 2
participant C3 as Controller 3
Note over C1,C3: 集群启动或 Leader 宕机
C1->>C2: RequestVote(term=1)
C1->>C3: RequestVote(term=1)
C2-->>C1: VoteGranted
C3-->>C1: VoteGranted
Note right of C1: C1 成为 Leader (term=1)
loop Heartbeat
C1->>C2: AppendEntries(term=1)
C1->>C3: AppendEntries(term=1)
end
Note over C1: C1 宕机
C2->>C3: RequestVote(term=2)
C3-->>C2: VoteGranted
Note right of C2: C2 成为新 Leader (term=2)
KRaft 使用标准 Raft 协议实现 Controller 高可用。
触发条件
- 集群首次启动;
- Active Controller 宕机或网络隔离;
- Follower 在
election.timeout.ms(默认 1--2 秒,随机化)内未收到心跳。
详细步骤
步骤 1:Follower 转为 Candidate
- 某 Follower(如 Controller 2)未收到 Leader 心跳;
- 自增 term(任期)(如 1 → 2);
- 投票给自己;
- 向其他 Quorum 成员发送
RequestVote RPC,包含:- 自身 term;
- 最后一条日志的 index 和 term。
步骤 2:接收投票
- 其他节点判断:
- 自身 term ≤ 请求 term;
- 请求者日志 不比自己旧;
- 若满足,则投出 唯一一票;
- 返回
VoteGranted = true。
步骤 3:赢得选举
- Candidate 收到 多数派投票(3 节点需 2 票);
- 转为 Leader;
- 向所有 Follower 发送空
AppendEntries(心跳),宣告领导权。
步骤 4:维持领导
- Leader 定期(如每 500ms)发送心跳;
- Follower 收到后重置选举计时器;
- 只要心跳正常,就不会发起新选举。
步骤 5:处理旧 Leader 回归
- 原 Leader 恢复但 term 已过期;
- 收到新 Leader 的
AppendEntries后,自动降级为 Follower; - Raft 协议天然避免脑裂。
Raft 安全性保障(KRaft 实现)
- Election Safety:任一 term 最多一个 Leader;
- Log Matching:相同 index + term 的日志内容一致;
- Leader Completeness:已提交日志必存在于未来 Leader 中;
- Pre-Vote(可选):防止网络抖动引发无效选举。
五、KRaft 配置详解
1. 生成集群 ID(首次部署必需)
bash
CLUSTER_ID=$(bin/kafka-storage.sh random-uuid)
echo $CLUSTER_ID # 保存用于格式化
2. server.properties(Combined Mode 示例)
properties
# ===== 基础身份 =====
process.roles=broker,controller
node.id=1
# ===== 网络监听 =====
listeners=PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
inter.broker.listener.name=PLAINTEXT
controller.listener.names=CONTROLLER
advertised.listeners=PLAINTEXT://kafka1.example.com:9092
# ===== Controller Quorum =====
controller.quorum.voters=1@kafka1:9093,2@kafka2:9093,3@kafka3:9093
# ===== 存储目录 =====
log.dirs=/data/kafka/data
metadata.log.dir=/data/kafka/metadata # 强烈建议独立磁盘
# ===== 主题默认配置 =====
num.partitions=6
default.replication.factor=3
min.insync.replicas=2
# ===== 安全关闭 =====
controlled.shutdown.enable=true
3. 初始化存储目录(每个节点执行)
bash
bin/kafka-storage.sh format -t <CLUSTER_ID> -c config/server.properties
4. 启动服务
bash
bin/kafka-server-start.sh config/server.properties
# 无需启动 ZooKeeper!
六、关键配置参数说明
| 参数 | 默认值 | 说明 | 生产建议 |
|---|---|---|---|
process.roles |
--- | 节点角色 | broker,controller(Combined) |
node.id |
--- | 节点唯一 ID | 与 voters 中 ID 一致 |
controller.quorum.voters |
--- | Quorum 列表 | 格式:id@host:port,... |
metadata.log.dir |
同 log.dirs |
元数据日志目录 | 独立 SSD 磁盘 |
metadata.max.retention.bytes |
Long.MAX | 元数据日志保留大小 | 按需设置(如 1GB) |
controller.quorum.election.timeout.ms |
1000--2000 | 选举超时(随机) | 无需调整 |
七、运维与监控
1. 查看 Quorum 状态
bash
bin/kafka-metadata-quorum.sh --bootstrap-server kafka1:9092 describe --status
输出示例:
makefile
ClusterId: a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8
LeaderId: 1
VoterEndpoints: 1@kafka1:9093,2@kafka2:9093,3@kafka3:9093
HighWatermark: 12345
2. 查看元数据日志(调试)
bash
bin/kafka-dump-log.sh --cluster-metadata-decoder \
--files /data/kafka/metadata/00000000000000000000.log
3. 关键监控指标
| 指标 | 说明 |
|---|---|
ActiveControllerCount |
应恒为 1 |
QueuedUnsentRequests |
Controller 请求积压 |
AppendRecordsRate |
元数据写入速率 |
MetadataLogSize |
元数据日志大小(应定期快照压缩) |
八、常见问题与故障处理
| 问题 | 现象 | 解决方案 |
|---|---|---|
| 无法选举 Leader | ActiveControllerCount = 0 |
检查 Quorum 节点数 ≥ 半数 |
| Broker 无法注册 | 日志报 "NotControllerException" | 检查 node.id 唯一性、网络通 CONTROLLER 端口 |
| 元数据日志损坏 | 启动失败,报 "Corrupt snapshot" | 从 Snapshot 恢复,或重建集群(需备份数据) |
| 客户端连接失败 | 报 "UnknownTopicOrPartition" | 确认 Active Controller 正常,元数据已同步 |
九、总结与最佳实践
推荐场景
- 新项目:强制使用 Kafka 3.5+ + KRaft;
- 容器化部署:Kubernetes StatefulSet + Combined Mode;
- 大规模集群:Dedicated Controller Mode + 独立元数据盘。
注意事项
- 不可原地升级:ZooKeeper 模式需重建集群才能迁移到 KRaft;
- 备份策略 :定期备份
metadata.log.dir和 Topic 数据; - 监控重点:Quorum 健康度、元数据日志大小、选举频率。