Kafka + KRaft模式架构基础介绍

13 阅读5分钟

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 ModeController 与 Data Broker 物理分离超大集群(> 20 节点)或高安全要求

三、Combined Mode 架构与工作流程

架构图

graph LR
    subgraph "KRaft Controller Quorum"
        C1[Controller 1<br><b>Active</b>]
        C2[Controller 2<br>Follower]
        C3[Controller 3<br>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 = B1C2 = B2C3 = B3,即角色合一。

工作流程步骤

  1. 集群启动

    • 所有节点启动 Kafka 服务;
    • 每个节点根据 process.roles=broker,controller 初始化双重角色;
    • Controller 模块尝试加入 Raft Quorum。
  2. Raft 选举

    • 节点通过 CONTROLLER 监听器(如 9093 端口)通信;
    • 选举出一个 Active Controller(Leader)
    • 其余节点成为 Follower,仅同步元数据日志。
  3. Broker 注册

    • 每个 Broker 向 Active Controller 发送注册请求;
    • Active Controller 将 Broker 信息写入 Metadata Log(内部 Topic __cluster_metadata);
    • 日志通过 Raft 协议复制到所有 Follower。
  4. 元数据变更(如创建 Topic)

    • 客户端请求 → 任意 Broker → 转发至 Active Controller;
    • Active Controller 生成元数据变更记录,追加到 Metadata Log;
    • 日志复制成功后,广播新元数据给所有 Broker。
  5. 消息读写(数据平面)

    • Producer/Consumer 连接任意 Broker;
    • 若非目标分区 Leader,Broker 返回 Redirect;
    • 客户端直连 分区 Leader Broker不经过 Controller
    • 消息路径完全独立于控制平面
  6. 故障恢复

    • 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(首次部署必需)

CLUSTER_ID=$(bin/kafka-storage.sh random-uuid)
echo $CLUSTER_ID  # 保存用于格式化

2. server.properties(Combined Mode 示例)

# ===== 基础身份 =====
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. 初始化存储目录(每个节点执行)

bin/kafka-storage.sh format -t <CLUSTER_ID> -c config/server.properties

4. 启动服务

bin/kafka-server-start.sh config/server.properties
# 无需启动 ZooKeeper!

六、关键配置参数说明

参数默认值说明生产建议
process.roles节点角色broker,controller(Combined)
node.id节点唯一 IDvoters 中 ID 一致
controller.quorum.votersQuorum 列表格式:id@host:port,...
metadata.log.dirlog.dirs元数据日志目录独立 SSD 磁盘
metadata.max.retention.bytesLong.MAX元数据日志保留大小按需设置(如 1GB)
controller.quorum.election.timeout.ms1000–2000选举超时(随机)无需调整

七、运维与监控

1. 查看 Quorum 状态

bin/kafka-metadata-quorum.sh --bootstrap-server kafka1:9092 describe --status

输出示例

ClusterId: a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8
LeaderId: 1
VoterEndpoints: 1@kafka1:9093,2@kafka2:9093,3@kafka3:9093
HighWatermark: 12345

2. 查看元数据日志(调试)

bin/kafka-dump-log.sh --cluster-metadata-decoder \
  --files /data/kafka/metadata/00000000000000000000.log

3. 关键监控指标

指标说明
ActiveControllerCount应恒为 1
QueuedUnsentRequestsController 请求积压
AppendRecordsRate元数据写入速率
MetadataLogSize元数据日志大小(应定期快照压缩)

八、常见问题与故障处理

问题现象解决方案
无法选举 LeaderActiveControllerCount = 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 健康度、元数据日志大小、选举频率。

延伸阅读