Kafka 的部署单元包括控制器、保存分区副本的 Broker,以及运行生产者、消费者和数据管道的应用进程。三类进程承担不同负载,扩容和故障恢复也有不同边界。只画几个 Broker,容易漏掉元数据多数派、客户端网络地址和内部主题这些影响可用性的条件。

本文以 Apache Kafka 4.3 文档中的 KRaft 部署为参照。Kafka 4.0 起不再支持 ZooKeeper 模式;旧集群迁移需要经过兼容的 3.x 版本。图中的三控制器、三 Broker 是解释故障域的示例,不是所有业务的容量配置。

控制面与数据面

控制器维护 Broker 注册、主题和分区元数据,决定分区领导者等集群状态。Broker 保存业务日志副本并响应生产和消费请求。业务消息无需经过控制器转发,控制器的副本数也不决定业务主题的副本数。

图 1:KRaft 控制面与业务数据面的部署边界

flowchart TB
  subgraph CP[控制面:独立控制器进程]
    C1[Controller 1] --- C2[Controller 2]
    C2 --- C3[Controller 3]
  end
  subgraph DP[数据面:独立 Broker 进程]
    B1[Broker 1] --- B2[Broker 2]
    B2 --- B3[Broker 3]
  end
  DP -. 注册、心跳与元数据同步 .-> CP
  P[生产者应用] -->|Produce| DP
  DP -->|Fetch 响应| U[消费者应用]

图 1 中的虚线是管理通信,实线表示业务数据路径或组内关系。独立进程允许分别重启和扩容两类角色;合并模式会把 Broker 的资源争用和重启同时带给控制器,适合开发验证,关键生产环境应拆开部署。KRaft 部署说明给出了角色与多数派要求。

故障域与副本放置

三个控制器要分散到独立故障域,三个投票成员需要两个存活才能维持多数派。业务主题也应按故障域放置副本;配置 broker.rack 后,还需要检查实际分区分配,不能只凭副本因子判断容灾能力。

图 2:三个故障域中的控制器与单分区副本

flowchart LR
  subgraph AZA[故障域 A]
    CA[Controller A]
    BA[Broker A:P0 Leader]
  end
  subgraph AZB[故障域 B]
    CB[Controller B]
    BB[Broker B:P0 Follower]
  end
  subgraph AZC[故障域 C]
    CC[Controller C]
    BC[Broker C:P0 Follower]
  end
  CA --- CB
  CB --- CC
  BA -->|日志复制| BB
  BA -->|日志复制| BC

图 2 中一个故障域退出后,还剩两个控制器和两个 P0 副本。能否继续写入仍取决于存活副本是否同步、min.insync.replicas、选举状态和网络连通性。跨可用区复制还会增加网络成本和确认延迟,跨地域则通常采用独立集群与镜像复制。副本布局依据见Broker 的 rack 配置。

客户端发现与监听地址

bootstrap.servers 是客户端发现集群的起点。拿到元数据后,客户端会连接目标分区的 Broker 地址。负载均衡器能够接受第一次连接,并不代表后续生产与消费可用。

图 3:从 bootstrap 发现到分区领导者直连

sequenceDiagram
  participant C as 客户端
  participant B1 as Bootstrap Broker
  participant B2 as P0 Leader Broker
  C->>B1: 请求元数据
  B1-->>C: P0 位于 B2,返回可连接地址
  C->>B2: Produce 或 Fetch
  B2-->>C: 写入结果或数据

图 3 的第二次连接使用 Broker 公布的地址。listeners 定义本机监听端点,advertised.listeners 定义对客户端公布的端点;后者必须能从对应客户端网络解析和访问。Controller listener、Broker 间通信和外部客户端入口要分别设计安全边界,不能把容器内部主机名直接公布给外部应用。监听配置与公布地址说明解释了这两个配置的区别。

分区数量与复制因子

分区切分业务日志,副本复制同一分区。一个 Broker 可以是 P0 的领导者,同时是 P1 的跟随者。新增 Broker 不会自动把现有主题平均搬过去,已有副本通常需要重分配。

图 4:三个分区在三个 Broker 上交错放置副本

flowchart TB
  T[orders:3 个分区,副本因子 2]
  T --> P0[P0:独立 offset 序列]
  T --> P1[P1:独立 offset 序列]
  T --> P2[P2:独立 offset 序列]
  P0 --> A[Broker A:P0 Leader / P2 Follower]
  P0 --> B[Broker B:P1 Leader / P0 Follower]
  P1 --> B
  P1 --> C[Broker C:P2 Leader / P1 Follower]
  P2 --> C
  P2 --> A

图 4 中一共是六份分区副本,而不是六个独立分区。把副本因子从二改成三会增加存储与复制开销,不会把普通消费者组的分区分配并行度从三提高到六。Kafka 设计文档分别描述了分区与副本机制。

写入确认与故障恢复

在三副本主题上,acks=all 配合 min.insync.replicas=2 可以要求写入满足至少两个同步副本的条件。acks=all 等待当前 ISR 的复制条件,并不等于固定等待所有配置副本,也不表示每台机器已同步刷盘。

图 5:三副本写入时的复制与确认路径

sequenceDiagram
  participant P as Producer
  participant L as Leader
  participant F1 as ISR Follower 1
  participant F2 as ISR Follower 2
  P->>L: Produce,acks=all
  L->>L: 追加本地日志
  F1->>L: Fetch
  L-->>F1: 新日志批次
  F2->>L: Fetch
  L-->>F2: 新日志批次
  F1->>L: 后续 Fetch 报告新位点
  F2->>L: 后续 Fetch 报告新位点
  L-->>P: 满足复制与最小 ISR 条件后确认

图 5 中复制由 follower 拉取,Producer 不逐个请求副本。生产故障检查应同时查看 ISR 和选举相关特性:Kafka 4.1 起新集群默认启用 ELR,安全候选者可能来自符合条件的 ISR 外副本,不能把所有 ISR 外选举都归为脏选举。具体边界见生产者确认配置和ELR 说明。

消费分配与组协调

普通 consumer group 内一个分区在同一时刻分配给一个成员;不同组可以独立消费同一分区。Group Coordinator 是某个 Broker 上的职责,不需要另起一个 Controller 进程。

图 6:两个消费者组独立读取同一组分区

flowchart LR
  P0[orders P0] --> A1[组 A:消费者 1]
  P1[orders P1] --> A2[组 A:消费者 2]
  P2[orders P2] --> A2
  P0 --> B1[组 B:消费者 1]
  P1 --> B1
  P2 --> B1
  A1 -. 组管理与位点提交 .-> G[Broker 上的 Group Coordinator]
  A2 -. 组管理与位点提交 .-> G

图 6 的组 A 可以扩容到三个有效成员,继续增加成员不会增加这三个分区的分配并行度。它讨论的是普通 consumer group,不包含 Share Group。Kafka 4.x 同时支持 classic 和新 consumer 协议;group.protocol=consumer 使用服务端分配与增量协调,classic 的 JoinGroup/SyncGroup 时序不适用于它。Consumer Rebalance Protocol说明了协议切换及配置边界。

三类内部状态

元数据、消费位点和事务状态拥有不同的存储与协调职责。某个业务 Broker 正常,不代表它所在集群的所有协调功能都正常。

图 7:集群元数据、消费位点与事务状态的归属

flowchart LR
  C[Controller quorum] --> M[专用元数据日志:__cluster_metadata]
  G[Broker 上的 Group Coordinator] --> O[内部主题:__consumer_offsets]
  T[Broker 上的 Transaction Coordinator] --> X[内部主题:__transaction_state]
  O --> D[Broker 的数据目录与副本]
  X --> D

图 7 把元数据日志与两个普通复制机制下的内部主题分开。__cluster_metadata 使用 KRaft 专用协议,不能按业务主题随意创建或消费。位点与事务主题也需要副本及容量规划;单 Broker 开发环境常见的降副本配置不能直接带入生产。Broker 配置列出了这两类内部主题的复制与最小 ISR 参数。

Kafka Connect 的运行位置

Connect worker 是独立于 Broker 的 JVM 进程。Connector 描述连接配置并生成 task 配置,task 执行搬运;扩容 worker 提供运行槽位,但 task 数量仍受连接器和源系统的切分能力限制。

图 8:Connect worker、任务与外部数据源的关系

flowchart LR
  DB[外部数据源] --> S[Worker A:Source Task]
  S --> K[Kafka 业务主题]
  K --> T[Worker B:Sink Task]
  T --> OUT[外部存储]
  API[管理端 REST API] -. 配置 Connector .-> S
  S -. 配置、状态与 Source 位点 .-> I[Connect 内部主题]
  T -. 配置与状态 .-> I

图 8 的 Source 位点与业务消费者组位点不同。分布式 Connect 利用 Kafka 内部主题保存配置、状态和 Source 位点;Sink 消费位置使用消费者组机制。Source exactly-once 还要求连接器自身支持,框架开关不能为任意外部数据源制造事务能力。Connect 用户指南给出了模式与一致性条件。

Kafka Streams 的运行位置

Kafka Streams 是嵌入业务应用的库。部署的是应用实例,不是附加到 Broker 上的计算服务。任务分配给应用的流线程,持久状态可以保存在本地 RocksDB,并通过 changelog 恢复。

图 9:Streams 应用中的任务、本地状态与恢复日志

flowchart LR
  IN[输入主题] --> TASK[应用 JVM:StreamThread / Task]
  TASK --> OUT[输出主题]
  TASK <--> STORE[本地 State Store]
  TASK -->|状态变更| LOG[Kafka changelog 主题]
  LOG -. 故障后恢复 .-> NEW[接管任务的应用实例]
  TASK -. 需要重分区时 .-> RP[Kafka repartition 主题]

图 9 中应用扩容影响计算任务的分配,Broker 扩容影响日志存储和服务能力,两者不能互相替代。恢复时间与状态规模、changelog 读取速度和 standby 配置有关,增加实例并不自动消除状态恢复成本。Streams 架构描述了任务、线程与容错关系。

事务覆盖的范围

Kafka 事务能把输出记录与输入消费位点关联到同一次提交,事务消费者以 read_committed 读取结果。外部数据库和 HTTP 服务不会自动加入这个事务。

图 10:Kafka 事务内的输出与位点及事务外的副作用

flowchart TB
  A[消费并处理输入]
  subgraph TX[单个 Kafka 集群的事务范围]
    O[输出分区记录]
    P[输入消费位点]
    C[事务协调与提交标记]
    C --> O
    C --> P
  end
  A --> O
  A --> P
  A --> DB[外部数据库写入:独立一致性边界]
  O --> R[read_committed 消费者]

图 10 的数据库箭头跨出了事务范围,重试处理可能再次执行它。需要数据库幂等键、Outbox 或目标系统自身的事务协议来处理这条路径。Kafka 的提交结果也不等于多个消费者在同一瞬间看见所有输出。消息交付语义解释了 exactly-once 对外部存储的要求。

跨地域复制与切换

跨地域容灾常用两个独立 Kafka 集群,MirrorMaker 2 基于 Connect 复制数据与相关检查点。目标端复制落后时,源集群已确认的数据可能尚未抵达目标。

图 11:独立地域集群之间的异步镜像与消费切换

flowchart LR
  P[生产应用] --> A[地域 A:Kafka 集群]
  A -->|读取源主题| MM[MirrorMaker 2 / Connect]
  MM -->|异步复制| B[地域 B:Kafka 集群]
  MM -. 检查点与位点映射 .-> B
  A --> C1[A 地消费者]
  B -. 切换后重新定位 .-> C2[B 地消费者]

图 11 的目标 offset 不应直接当成源 offset。切换方案需要验证位点映射、主题命名、ACL、重复处理和复制延迟,RPO 也要按实际未复制数据量评估。MirrorMaker 2 不提供跨集群同步事务。跨集群镜像文档介绍了这类独立集群拓扑。

监控沿职责分层

Producer 超时、消费落后和控制器失去多数派可能同时出现,但处置入口不同。控制面看元数据领导者与同步状态,数据面看离线分区、ISR、磁盘和请求队列,应用端看发送错误、消费位点和业务完成时间。

图 12:从业务现象定位控制面、数据面与应用故障

flowchart TB
  S[业务写入失败或消费延迟] --> Q{主要异常在哪里}
  Q -->|创建主题或选举异常| C[检查 Controller quorum 与元数据滞后]
  Q -->|Produce / Fetch 错误| B[检查离线分区、ISR、磁盘与监听地址]
  Q -->|应用处理变慢| A[检查 consumer lag、线程与下游耗时]
  C --> V[恢复后验证真实写入、读取与业务结果]
  B --> V
  A --> V

图 12 的分支是排查入口,不能据此把多个故障域割裂。滚动维护前需确认剩余控制器多数派、分区同步副本和磁盘容量,单个节点恢复后再处理下一个。验收应包括一条可追踪记录的生产、消费与业务结果;进程存活和监控数值恢复不足以证明全链路恢复。监控文档列出了各层指标。

系列导航

本篇为 Kafka 系列第 18 篇。完整目录见系列导读。

相关章节 主题
01 架构总览:Broker、Controller 与元数据管理
07 副本与 ISR:高可用的代价和折中
08 Controller 与 KRaft:从 ZooKeeper 到内置共识
16 生产运维:集群扩缩、监控指标与故障排查
17 Kafka 与 RocketMQ:两种消息系统的设计选择

参考资料

文中链接均指向 Apache Kafka 官方文档的 4.3 版本页。部署时还应以实际 Broker、客户端和已启用的 feature level 为准,不能用网页版本代替集群状态。