什么是 Kafka
Apache Kafka 是一个分布式事件流平台,最初由 LinkedIn 工程师 Jay Kreps 等人于 2010 年设计,2011 年开源并捐赠给 Apache 基金会。其名字来自作家 Franz Kafka——因为这个系统被设计为"优化写入",正如卡夫卡的写作风格一般充满流动感。
Kafka 的定位经历了三个阶段的演进:
| 阶段 | 定位 | 核心能力 |
|---|---|---|
| 第一代(2011) | 消息队列(MQ) | 解耦生产者与消费者,异步通信 |
| 第二代(2014) | 发布/订阅系统 | 多消费者组并发消费,消息持久化 |
| 第三代(2017+) | 事件流平台 | Streams 流处理、Connect 集成、Exactly-Once 语义 |
Kafka vs 传统 MQ:传统消息队列(如 RabbitMQ)的消息被消费后即删除。Kafka 的消息按配置的时间/大小保留(默认 7 天),任何消费者组都可以从任意 Offset 重新消费——这使得 Kafka 更像一个可重放的事件日志,而非一次性管道。
核心架构组件
Broker
Broker 是 Kafka 集群中的服务节点,负责接收 Producer 发来的消息、将消息持久化到磁盘,以及向 Consumer 提供读取服务。每个 Broker 都有唯一的 broker.id(整数)标识。生产环境通常部署 3 个或更多 Broker 以保证高可用。
Topic
Topic 是消息的逻辑分类,类似于数据库中的"表名"或文件系统中的"目录"。Producer 向特定 Topic 写入消息,Consumer 订阅 Topic 进行消费。Topic 名称在集群内唯一,命名通常采用 业务.子系统.事件类型 格式,如 ecommerce.order.created。
Partition(分区)
每个 Topic 被分割为多个 Partition,这是 Kafka 实现高吞吐量和水平扩展的核心机制。每个 Partition 是一个有序、不可变的消息序列,消息以追加写入的方式添加到末尾,并被分配一个从 0 开始单调递增的整数编号——即 Offset。
- 分区数决定了并行消费的最大能力(一个分区同一时刻只能被消费组中的一个 Consumer 消费)
- 分区内消息有序,跨分区不保证顺序
- 分区数一旦增加无法减少(只能增加)
Replica(副本)与 Leader/Follower
每个 Partition 可以配置多个 Replica(副本因子,replication-factor),分布在不同的 Broker 上。副本中有且只有一个 Leader,负责处理所有读写请求;其余副本为 Follower,持续从 Leader 同步数据。当 Leader 所在 Broker 宕机时,从 ISR 中选出新的 Leader。
Kafka 的独特设计哲学
顺序写磁盘
Kafka 将消息追加写入磁盘文件(顺序 I/O),而非随机写入。现代磁盘的顺序写速度可达 600 MB/s,与内存的顺序读写性能相近,远优于随机写的 ~100 次/秒。这是 Kafka 高吞吐量的底层基础之一。
零拷贝(Zero-Copy)
传统数据发送需要 4 次数据拷贝:磁盘 → 内核缓冲区 → 用户空间 → Socket 缓冲区 → 网卡。Kafka 利用 Linux 的 sendfile() 系统调用,跳过用户空间,只需 2 次拷贝(磁盘 → 内核缓冲区 → 网卡),显著降低 CPU 开销和延迟。
PageCache 利用
Kafka 不在 JVM 堆中缓存消息,而是依赖操作系统的 PageCache(页缓存)。当 Consumer 消费最近写入的消息时,数据通常仍在 PageCache 中,无需从磁盘读取,实现了"写后即读"的高速访问。这也意味着 Kafka 进程重启后,PageCache 中的数据不会丢失(由 OS 管理)。
与 RabbitMQ / RocketMQ 对比
| 特性 | Kafka | RabbitMQ | RocketMQ |
|---|---|---|---|
| 吞吐量 | 百万级 msg/s | 万级 msg/s | 十万级 msg/s |
| 消息保留 | 按时间/大小保留(默认7天) | 消费即删除 | 消费即删除(可配置) |
| 消息回溯 | 支持(任意 Offset) | 不支持 | 支持(时间点) |
| 消费模式 | Pull(拉取) | Push(推送) | Push + Pull |
| 消费顺序 | 分区内有序 | 队列内有序 | 分区内有序 |
| 延迟 | 毫秒级(低吞吐场景可达亚毫秒) | 微秒级 | 毫秒级 |
| 协议 | 自有协议(TCP) | AMQP | 自有协议 |
| 生态 | 最丰富(Flink/Spark/Debezium) | 较丰富 | 阿里云生态 |
ZooKeeper vs KRaft
Kafka 长期依赖 Apache ZooKeeper 存储集群元数据(Broker 注册、Topic 分区信息、Consumer Offset 等)。Kafka 3.x 引入了 KRaft(Kafka Raft Metadata Mode),将元数据管理内置化,彻底移除 ZooKeeper 依赖。
| 特性 | ZooKeeper 模式 | KRaft 模式(3.x) |
|---|---|---|
| 元数据存储 | ZooKeeper 集群 | Kafka 内置 Raft 日志 |
| 部署复杂度 | 需单独维护 ZK 集群(3节点) | 单进程,无外部依赖 |
| Controller 选举 | ZK 临时节点竞争 | Raft 算法 |
| 分区数上限 | ~20 万(ZK 性能瓶颈) | 数百万(理论) |
| 生产可用 | 稳定(老版本) | Kafka 3.3+ 生产可用 |
新项目推荐 KRaft 模式:Kafka 4.0 计划完全移除 ZooKeeper 支持。新建集群应直接使用 KRaft 模式(--bootstrap-controller)。现有 ZooKeeper 集群可通过迁移工具(kafka-storage.sh)无停机升级。
核心名词解释
-
Offset
消息在 Partition 中的唯一序号,从 0 开始单调递增。Consumer 通过记录"已消费到哪个 Offset"来追踪消费进度。Offset 在 Kafka 内部存储于
__consumer_offsetsTopic。 -
Consumer Group
一组共享相同
group.id的 Consumer 实例。Kafka 保证同一个 Consumer Group 中,每个 Partition 只被一个 Consumer 消费;不同 Consumer Group 之间互相独立,可以各自完整消费所有消息。这是 Kafka 实现"广播"和"负载均衡"的核心机制。 - Lag 消费延迟,等于 Partition 最新 Offset(Log End Offset)减去该 Consumer Group 已提交的 Offset。Lag 过高意味着消费者处理速度跟不上生产速度,是需要关注的关键监控指标。
- Commit Log Kafka 底层存储结构的本质——一个只能追加写入的有序日志文件。每个 Partition 对应磁盘上的一系列日志段文件(.log),消息永远追加到末尾,已写入的消息不可修改,这赋予了 Kafka "单一真相来源"的特性。
-
ISR
In-Sync Replicas,与 Leader 保持同步的副本集合。Follower 若在
replica.lag.time.max.ms(默认 30s)内未发送 Fetch 请求,或落后 Leader 超过阈值,则从 ISR 中移除。Leader 选举只从 ISR 中产生,以保证数据不丢失。 - Rebalance 当 Consumer Group 成员发生变化(新增/退出/崩溃)或 Topic 分区数变化时,Kafka 重新分配分区给各 Consumer 的过程。Rebalance 期间 Consumer Group 暂停消费,是影响系统稳定性的关键事件,需要尽量减少其频率。
关键理解:Kafka 中 Consumer Group 数量 = 并发消费者组数,Consumer 实例数(单个 Group 内)= 并行消费线程数,且受 Partition 数量上限约束。若 Consumer 数量超过 Partition 数量,多余的 Consumer 将处于空闲状态。