前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住给大家分享一下。点击跳转到网站:https://www.captainai.net/dongkelun
前言
本文从概念层面介绍 Kafka 是什么、整体架构、核心概念(Topic、Partition、Replica、ISR、消费组)以及一条消息从生产到消费的完整流转路径。
版本
Kafka 2.8.2
一、Kafka 是什么
Kafka 最初由 LinkedIn 开发,后捐赠给 Apache 基金会,是一个用 Scala/Java 编写的分布式事件流平台。它常被称作消息队列,但更准确的定位是分布式提交日志平台——核心是一个高吞吐、可持久化、可分区的追加式日志。
它解决的核心问题:
- 高吞吐的消息发布与订阅:单机每秒可处理数十万条消息
- 消息持久化:消息落盘,消费后按 retention 策略保留一段时间,支持多消费者重复消费
- 水平扩展:通过分区把负载分散到多个 Broker
- 故障容忍:通过副本机制,单个 Broker 宕机不影响服务
和传统消息队列(如 RabbitMQ)相比,Kafka 的设计取向不同:RabbitMQ 侧重灵活路由和即发即收,消息被消费后即删除;Kafka 侧重日志的持久化和顺序,消息按 offset 保留,消费者可以任意回退重读。这个差异决定了后面所有核心概念的设计。
二、整体架构
Kafka 集群的核心组件:
1 | ┌─────────────────────────────────────────┐ |
- Producer:消息生产者,把消息发送到指定 Topic 的某个分区
- Broker:集群中的单个服务节点,一个集群由一个或多个 Broker 组成,所有分区的副本都分布在 Broker 上
- Consumer:消息消费者,从 Broker 拉取消息
- ZooKeeper:2.8.2 版本中 Kafka 仍依赖 ZooKeeper 存储集群元数据(Topic、分区、副本分布)、选举 Controller、管理 Broker 上下线。KRaft 模式可去掉 ZooKeeper,但 2.8.2 中还是实验特性
- Controller:集群中某个 Broker 被选举为 Controller,负责管理所有分区的 Leader 选举和 ISR 变更。它不是独立进程,而是某个 Broker 承担的职责
三、核心概念
3.1 Topic 与 Partition
Topic 是消息的逻辑分类,生产者向 Topic 发消息,消费者从 Topic 收消息。
Topic 在物理上被拆分成多个 Partition(分区)。每个 Partition 是一个有序的、追加式的日志,消息按到达顺序写入,每条消息在分区内有一个唯一的、单调递增的序号 Offset。
1 | Topic: orders |
分区的作用:
- 并行度:一个 Topic 的多个分区可以分布在多个 Broker 上,由多个消费者并行处理,吞吐量随分区数扩展
- 顺序保证:Kafka 只保证分区内的消息顺序,不保证跨分区顺序。需要全局有序只能单分区,牺牲吞吐
- 扩展性:数据量增长时可以增加分区数,但已有数据的 Topic 改分区数会改变 key 到分区的映射,需谨慎
3.2 消息与 Offset
一条消息(Record)包含:
- Key:可选,决定消息落到哪个分区(相同 key 落到同一分区,保证同 key 有序)。不指定 key 时分散到各分区(先粘住一个分区攒 batch,换新 batch 时随机换分区),不保证顺序
- Value:消息体
- Timestamp:时间戳
Offset 是消息在分区内的唯一位置标识,是 Kafka 实现可重读、可回退的基础——消费者记住自己消费到的 offset,故障后从该 offset 继续,而不是像传统队列那样消费即删除。
3.3 副本:Leader 与 Follower
每个分区有多个副本(由 replication-factor 决定),分布在不同 Broker 上:
- Leader:处理该分区的所有读写请求
- Follower:从 Leader 拉取数据,被动复制,不直接服务请求
RF=1 时分区只有一个副本,这个副本自己就是 Leader,没有 Follower。此时完全没有冗余:Broker 宕机后分区不可用,也没有副本可选举为新 Leader;acks=all 退化为 acks=1(只有 Leader 自己确认)。RF=1 只适合测试或可丢弃数据,生产环境至少 RF=2(一般 RF=3)。
当 Leader 所在的 Broker 宕机时,Controller 会从 ISR 中选举新的 Leader(见 3.4)。
3.4 ISR(In-Sync Replicas)
ISR 是与 Leader 保持同步的副本集合。一个 Follower 要留在 ISR 中,必须在规定时间内追上 Leader 的最新 offset;长时间落后会被移出 ISR。
ISR 的意义:
- 选主范围:Leader 故障时只从 ISR 中选新 Leader,保证新 Leader 有较完整的数据
- 可用性与一致性的权衡:
min.insync.replicas要求至少 N 个副本确认才算写入成功,配合acks=all可在 Broker 宕机时不丢已确认的数据
3.5 消费组(Consumer Group)
消费组是 Kafka 实现负载均衡和水平扩展的核心机制:
- 多个消费者组成一个消费组,共同消费一个 Topic
- Topic 的每个分区只分配给组内的一个消费者
- 组内消费者数量不超过分区数时,每个消费者分到若干分区;消费者数量超过分区数时,多出的消费者空闲
1 | 消费组 A(3 个消费者)消费 Topic orders(3 个分区): |
不同消费组之间互不影响:组 A 和组 B 消费同一个 Topic,各自维护独立的 offset,相当于同一份数据被两套逻辑独立消费。这也是 Kafka 支持一份数据多路消费的方式。
当组内消费者加入、离开或宕机时,会触发 Rebalance 重新分配分区。
四、消息的完整流转
一条消息从生产到消费的完整路径:
1 | 1. Producer 发送消息(指定 Topic,可选 Key) |
几个关键点:
- 读写都只走 Leader,Follower 不直接服务客户端。写入经过 Leader 追加,消费从 Leader 拉取
- 消费者是拉模式而非推模式——消费者自己控制拉取速率,避免被生产速度压垮
- offset 由消费者管理,提交到内部 Topic
__consumer_offsets,Kafka 不强制消费即删除
五、基本使用
用最常用的命令行工具快速体验。
创建 Topic(3 分区、2 副本):
1 | kafka-topics.sh --create \ |
查看 Topic 详情(各分区的 Leader、ISR、Replicas):
1 | kafka-topics.sh --describe --topic orders --bootstrap-server localhost:9092 |
输出(上面创建的 Topic,3 分区、2 副本,Broker ID 为 1 和 2):
1 | Topic: orders TopicId: mK9pQ2vL5nR8wT3yB6jH4c PartitionCount: 3 ReplicationFactor: 2 Configs: |
- Leader:Leader 所在的 Broker ID
- Replicas:所有副本所在的 Broker ID,第一个就是 Leader
- Isr:当前与 Leader 保持同步的副本(见 3.4)
输出里的 Broker ID 是节点在集群中的标识:server.properties 里配置 broker.id,不配置时 Kafka 自动生成(从 1001 开始),所以看到的数字不一定是 1、2、3。
控制台生产消息:
1 | kafka-console-producer.sh --topic orders --bootstrap-server localhost:9092 |
控制台消费消息(从头开始),加 print.* 参数展示消息的分区号、offset 和 key:
1 | kafka-console-consumer.sh --topic orders --from-beginning \ |
输出效果(各列以制表符分隔;生产时未指定 key,key 列为 null,正对应”Key:可选”;具体分区号取决于分区策略,可能不同):
1 | Partition:0 Offset:0 null order-1 |
--bootstrap-server 只需给一个 Broker 地址,客户端会自动发现集群其他节点。
列出所有 Topic:
1 | kafka-topics.sh --list --bootstrap-server localhost:9092 |
删除 Topic(需在 server.properties 中开启 delete.topic.enable=true):
1 | kafka-topics.sh --delete --topic orders --bootstrap-server localhost:9092 |
安全集群说明
生产环境 Kafka 通常开启 Kerberos/SASL 认证,此时 --bootstrap-server 系列的客户端需额外指定安全配置。各命令的传入方式不同:
| 命令 | 参数 | 示例 |
|---|---|---|
kafka-topics.sh |
--command-config <文件> |
--command-config /etc/kafka/conf/client.properties(文件内容见下文) |
kafka-configs.sh |
--command-config <文件> |
--command-config /etc/kafka/conf/client.properties(文件内容见下文) |
kafka-console-producer.sh |
--producer-property key=value |
--producer-property security.protocol=SASL_PLAINTEXT |
kafka-console-consumer.sh |
--consumer-property key=value |
--consumer-property security.protocol=SASL_PLAINTEXT |
/etc/kafka/conf/client.properties 文件内容示例:
1 | security.protocol=SASL_PLAINTEXT |
如果只是临时用一下不想建文件,可以用进程替换:
1 | kafka-topics.sh --bootstrap-server kafka-01:9092 \ |
进程替换对 Producer 和 Consumer 命令行同样适用:printf 输出当作文件传入。走 AdminClient 的命令(kafka-topics、kafka-configs)只支持通过配置文件传入,不支持行内 --producer-property/--consumer-property。
支持 --zookeeper 的只有管理类命令(kafka-topics、kafka-configs、kafka-reassign-partitions 等),它们直连 ZooKeeper,由 ZK 自身的 SASL/JAAS 机制认证,不受 security.protocol 影响。2.8.2 中 --zookeeper 已标记为 DEPRECATED,推荐统一使用 --bootstrap-server。
关于 --bootstrap-server 与 --broker-list
--bootstrap-server 是 2.8.2 推荐使用的连接参数,--broker-list 是旧参数,二者传的内容一样(host:port 列表),但支持范围不同:
| 命令 | --broker-list |
--bootstrap-server |
|---|---|---|
kafka-console-producer.sh |
✅ 已弃用 | ✅ 推荐 |
kafka-console-consumer.sh |
❌ | ✅ |
kafka-topics.sh |
❌ | ✅ |
kafka-configs.sh |
❌ | ✅ |
kafka-consumer-groups.sh |
❌ | ✅ |
kafka-delete-records.sh |
❌ | ✅ |
kafka-reassign-partitions.sh |
Broker ID 列表 | ✅ |
kafka-log-dirs.sh |
Broker ID 列表 | ✅ |
kafka-consumer-perf-test.sh |
✅ 已弃用 | ✅ 推荐 |
kafka-console-producer.sh 和 kafka-consumer-perf-test.sh 中的 --broker-list 已标记 DEPRECATED,两个参数同时指定时 --broker-list 被忽略。其他命令(kafka-topics、kafka-configs、kafka-consumer-groups 等)不支持 --broker-list,只能用 --bootstrap-server。
kafka-reassign-partitions.sh 和 kafka-log-dirs.sh 的 --broker-list 含义不同——传的是 Broker ID 列表(如 101,102,103),指定查询或迁移的目标节点,不是连接地址。
六、关键设计决策
理解 Kafka 的几个核心设计,能解释它为什么快、为什么适合做日志。
追加式日志(Append-Only Log)
消息只追加不修改,顺序写磁盘。顺序写比随机写快几个数量级,加上 OS 页缓存,Kafka 的吞吐接近内存级别。这是消息队列做成日志的根本原因。
分区提供并行
单分区是有序的日志,多分区把日志横向切开,分散到多 Broker、多消费者,实现水平扩展。顺序性和吞吐的矛盾通过分区内有序来调和。
副本提供容错
每份数据多个副本分布在不同 Broker,Leader 故障自动切换。配合 ISR 和 acks 机制,在可用性和数据可靠性之间可调。
拉模式(Pull)
消费者主动拉取,而非 Broker 推送。好处是消费者按自己的处理能力控制速率,天然背压;代价是消费者空闲时会有轮询延迟,可用 long polling 缓解。
零拷贝(Zero-Copy)
消费时数据从磁盘直接送到网络,不经过用户态拷贝,用 sendfile 系统调用完成。这是 Kafka 高吞吐的关键之一。
七、本篇小结
- Kafka 本质是分布式追加式日志平台,不只是消息队列
- 核心概念:Topic(逻辑分类)→ Partition(有序日志,并行单位)→ Replica/ISR(容错)→ Consumer Group(负载均衡)
- 消息流转:Producer 选分区 → 写 Leader → 副本同步 → 按 acks 确认 → Consumer 拉取 → 提交 offset
- 设计取向:追加日志、分区并行、副本容错、拉模式、零拷贝