理解 Apache Kafka 架构和原理
Kafka 被描述为“分布式提交日志”——一个存储有序事件流、可重放且高度容错的分布式系统。
1. 架构概述

| 组件 | 主要功能 | 
|---|---|
| Broker | Kafka 服务器存储和服务数据。 | 
| Topic | 逻辑数据流;划分为多个分区。 | 
| 分区 | 仅追加日志,偏移量递增,确保分区有序。 | 
| 生产者 | 向主题发送消息(键值对);键决定分区(哈希)。 | 
| 消费者 | 根据偏移量读取消息;不删除任何数据。 | 
| 消费者组 | 消费者组将分区均等划分,实现水平扩展。 | 
| 复制 | 每个分区有 1 个领导者和 n 个追随者;领导者接收写入操作,追随者进行复制以应对错误。 | 
| 控制器 | Broker 被选举为控制器(在新版本中取代 ZooKeeper),以协调领导者/追随者。 | 
2. 工作原理
a) 写入数据
- 
生产者发送记录 → Broker 分区的 Leader → 追加到日志。
 - 
消息根据配置刷新到磁盘(默认 7 天或写满时)。
 - 
复 制同步到跟随者(ack=all 以确保零丢失)。
 
b) 读取数据
- 
消费者持续 poll() 并提交偏移量(自动或手动)。
 - 
多个消费者组独立读取同一主题 - “重放”功能。
 
c) 保证顺序
- 仅在同一分区内;如果需要全局访问,请使用键或单个分区。
 
3. 与 RabbitMQ 快速比较
| 得分 | Kafka | RabbitMQ | 
|---|---|---|
| 模型 | 分区日志 | 队列 | 
| 排序 | 分区内 | 队列内(单个消费者) | 
| 保留 | 基于策略(天数,GB) | 基于确认(确认后删除) | 
| 规模 | 水平(添加代理) | 垂直(添加队列) | 
4. 关于 Apache Kafka 的常见问题
ZooKeeper 在 Kafka 系统中扮演什么角色?
Zookeeper,顾名思义,是管理整个 Kafka 系统的“管家”。以下是 Zookeeper 的一些主要角色:
- 存储集群范围的元数据
 - 活跃 Broker 列表
 - 主题、分区和副本分配列表
 - 每个分区的 ISR(同步副本)
 - ACL 和配额
 - 客户端(生产者/消费者)通过 Broker 读取这些元数据;Broker 从 Zookeeper 获取元数据。
 - 协调 Leader 选举
 - 每个分区有 1 个 Leader 和 n 个 Follower。
 - 当 Broker 宕机或 ISR 发生变化时,Broker 控制器(由 Zookeeper 选举)将触发 Leader 选举 → 为相应分区选出新的 Leader。
 - 集群配置与变更
 - 管理员创建主题、更改复制因子、ACL 等操作时,这些更改会记录在 Zookeeper 中 → 控制器更新并分发给 Broker。
 - 不执行任何操作
 - 不保存实际数据消息。
 - 不参与生产者/消费者发送/接收记录。
 - Zookeeper = Kafka 的“元数据大脑和协调器”(在 KRaft 之前的版本中);它确保集群知道“谁处于活动状态、谁是 Leader、哪个主题位于何处”,并在出现问题时自动重新平衡。
 
什么是 Kafka Raft?
Kafka Raft(简称 KRaft)是由 Apache Kafka 开发的共识协议,旨在完全消除对 ZooKeeper 集群元数据管理的依赖。它从 Kafka 3.3.1 开始部署。
KRaft 将 Kafka 转变为一个“独立的分布式流平台”——它内部使用 Raft 存储数据并管理元数据。
ZooKeeper 与 Kafka Raft 对比?
| 标准 | ZooKeeper 模式(Kafka 2.8 之前版本) | KRaft 模式(Kafka ≥ 2.8,4.0 起为默认模式) | 
|---|---|---|
| 角色 | Kafka 外部的元数据和 Leader 选举系统 | 通过 Raft 共识将元数据和控制器集成到 Kafka 中 | 
| 所需系统数量 | 2 个(Kafka + ZooKeeper 集群)→ 5-7 个 JVM | 1 个(仅 Kafka)→ 开发/测试仅需 1 个 JVM | 
| 配置 | zookeeper.connect、ACL ZooKeeper、SSL ZooKeeper… | controller.quorum.voters、controller.listener.names – 无需 ZK 配置 | 
| 元数据存储 | ZK znodes | 内部主题 __cluster_metadata(事件溯源,可重放) | 
| 分区数量有限 | ~200 k(由于 ZK 瓶颈) | >200 k(目标数百万) | 
| 分区创建/更改时间 | 线性,慢 | 恒定,快速(10 倍)– 42 秒 vs 重新分配 10 k 个分区时的 600 秒 | 
| 故障转移控制器 | 10-30 秒(取决于 ZK 会话超时) | 几乎瞬时,~1 秒 | 
| 安全性 | 必须维护 2 个模型:Kafka SASL/SSL + ZK ACL | 单一同步模型 | 
| 启动开发/测试 | 需要先启动 ZK | 在一个命令中运行 kafka-server-start.sh | 
| 动态配置 | 支持动态 advertised.listeners 和 leader.imbalance.* | 更改需要重启(静态配置) | 
| 迁移路径 | 不适用 | 如果升级到 Kafka 4.0 则需要 - 使用 kafka-storage 工具和 KIP-833 | 
我在 Kafka 中有 3 个主题,每个主题有 3 个分区,Kafka 会创建多少个分区?
Kafka 总共会创建 9 个分区:
3 个主题 × 3 个分区/主题 = 9 个分区。
Kafka 分区是队列吗?
不是。Kafka 分区不是传统的队列(FIFO);它是一个仅追加日志(提交日志):
- 
数据按顺序写入日志文件的末尾(偏移量递增)。
 - 
消费者仅按偏移量顺序读取;读取后不会删除任何数据。
 - 
没有类似队列的 pop()、poll() 和 peek() 方法;消息会一直保留在那里,直到保留策略删除(时间/字节)。
 
我们如何知道消费者已经消费了哪个偏移量?
消费者通过自行提交(或让 Kafka 替其提交)并将偏移量保存到特殊主题 __consumer_offsets 中来“记住”偏移量:
- 
批处理完成后,调用:
 - 
consumer.commitSync()– 阻塞直到收到 Broker 确认消息。 - 
consumer.commitAsync()– 不阻塞,但有一个回调。 - 
或者 enable.auto.commit=true(默认),Kafka 每 auto.commit.interval.ms 自动提交一次。
 - 
Broker 存储三元组:(group.id, topic, 分区) → __consumer_offsets 分区中的偏移量。
 - 
当 Consumer 重启时:
 - 
发送 OffsetFetch 请求以获取上次提交的偏移量。
 - 
从该偏移量继续轮询(如果尚未获取偏移量,则遵循 auto.offset.reset 策略)。
 - 
Consumer 无需在其端存储偏移量;偏移量始终由 Kafka 集中管理。
 
Kafka Broker 是服务器吗?
是的——Kafka Broker 是物理服务器或虚拟服务器,运行 JVM 进程(kafka-server-start)。
但是,可以:
- 
一台服务器运行一台 Broker(恰好 1:1)。
 - 
如果您想在个人计算机上测试多代理,请使用 1 个运行多个代理的服务器(更改端口、log.dirs)。
 
在实际生产中,为了简化操作,1 个代理 = 1 个服务器。
