Kafka 基础:从消息队列到事件流平台

学习目标

  1. 能说清 Kafka 是什么、适合什么、不适合什么。
  2. 能解释 broker、topic、partition、offset、consumer group 的关系。
  3. 能用命令创建 topic、发送消息、消费消息、查看消费组状态。

Kafka 是什么

Kafka 是一个分布式事件流平台。它表面上像消息队列,但核心模型不是"把消息投递给某个消费者后立刻删除",而是"把事件追加写入一个可持久化、可分区、可复制的日志中,消费者按 offset 自己读取"。

这带来几个关键差异:

对比项 普通队列视角 Kafka 事件流视角
数据生命周期 消费后通常删除 按保留策略保存,可回放
消费进度 队列维护投递状态 消费者组维护 offset
扩展方式 增加队列或消费者 增加 partition、broker、consumer
典型能力 异步解耦 异步解耦 + 数据总线 + 回放 + 流计算

Kafka 适合什么

Kafka 适合高吞吐、可回放、多订阅方、事件流式处理的场景:

  • 订单事件:订单创建后,库存、积分、优惠券、风控系统分别订阅。
  • 用户行为日志:Web/App 埋点进入 Kafka,再进入实时计算、数据湖、画像系统。
  • 数据同步:业务数据库变更通过 CDC 写入 Kafka,再分发到搜索、缓存、数仓。
  • IoT 上报:设备持续上报状态,Kafka 接住洪峰,后端服务按能力处理。
  • 实时指标:实时计算 UV、PV、支付成功率、异常告警。

Kafka 不适合什么

Kafka 不是所有队列场景的默认答案:

不适合场景 原因 替代思路
极低延迟 RPC Kafka 是日志系统,不是请求响应框架 HTTP/gRPC
复杂任务调度 Kafka 不负责延迟队列、任务状态机、重试编排 Quartz、XXL-JOB、Temporal
小团队简单异步 运维成本可能高于收益 Redis Stream、RabbitMQ、云队列
强事务跨系统一致性 Kafka 事务只覆盖 Kafka 内部和部分生产者语义 本地事务表、Saga、Outbox

核心组件

Broker

Broker 是 Kafka 服务节点。一个 Kafka 集群由多个 broker 组成。每个 broker 存储若干 partition 的日志数据,处理客户端读写请求,并参与副本复制。

Topic

Topic 是消息分类。比如:

  • order-events:订单事件。
  • payment-events:支付事件。
  • user-behavior:用户行为日志。

Topic 不是一个单文件队列,而是由多个 partition 组成。

Partition

Partition 是 Kafka 扩展吞吐和并行消费的基本单位。每个 partition 内部是有序追加日志;不同 partition 之间不保证全局顺序。

如果 topic 有 6 个 partition,一个消费组最多可以让 6 个消费者实例并行消费。第 7 个消费者会空闲,因为同一个消费组内,一个 partition 同一时刻只能分配给一个消费者。

Offset

Offset 是消息在 partition 内的位置编号。消费者提交 offset 表示"我已经处理到哪里"。Kafka 保存的是日志,消费者保存的是进度。

Consumer Group

Consumer Group 是一组共同消费某个 topic 的消费者。不同消费组之间互不影响,都会读到同一份消息。

例子:

  • inventory-service 消费组处理库存。
  • coupon-service 消费组处理优惠券。
  • risk-service 消费组处理风控。

三者都订阅 order-events,但各自维护自己的 offset。

Kafka 架构关系

Producer
Topic: order-events
Partition 0
Partition 1
Partition 2
Broker 1
Broker 2
Broker 3
Consumer A / group inventory
Consumer B / group inventory
Consumer C / group risk

第一组实操:启动 Kafka 并收发消息

进入 CLI demo:

bash 复制代码
cd kafka-knowledge-system/demos/cli-kafka-lab
docker compose up -d
docker compose ps

创建 topic:

bash 复制代码
docker compose exec kafka kafka-topics \
  --bootstrap-server localhost:9092 \
  --create \
  --topic order-events \
  --partitions 3 \
  --replication-factor 1

查看 topic:

bash 复制代码
docker compose exec kafka kafka-topics \
  --bootstrap-server localhost:9092 \
  --describe \
  --topic order-events

发送消息:

bash 复制代码
docker compose exec -T kafka kafka-console-producer \
  --bootstrap-server localhost:9092 \
  --topic order-events <<'EOF'
{"orderId":"O1001","status":"CREATED","amount":99.8}
{"orderId":"O1002","status":"PAID","amount":199.0}
EOF

消费消息:

bash 复制代码
docker compose exec kafka kafka-console-consumer \
  --bootstrap-server localhost:9092 \
  --topic order-events \
  --from-beginning \
  --group order-demo-group \
  --timeout-ms 5000

查看消费组:

bash 复制代码
docker compose exec kafka kafka-consumer-groups \
  --bootstrap-server localhost:9092 \
  --describe \
  --group order-demo-group

验证标准

验证项 命令 预期结果
Kafka 正常启动 docker compose ps kafka 状态为 running
Topic 创建成功 kafka-topics --describe 能看到 3 个 partition
消息写入成功 console producer 无报错
消息消费成功 console consumer 能打印 JSON 消息
Offset 已提交 kafka-consumer-groups --describe CURRENT-OFFSET 大于 0

常见误区

  1. 认为 topic 有序。准确说法是:partition 内有序,topic 级别默认不保证全局有序。
  2. 认为消息消费后删除。准确说法是:Kafka 按保留时间或大小删除,与是否消费无直接关系。
  3. 认为消费者越多越快。准确说法是:同一消费组内并行度上限受 partition 数量限制。
  4. 认为 Kafka 一定不丢消息。准确说法是:可靠性取决于 producer ack、broker 副本、consumer offset 提交策略。

02 Topic、分区、生产者和消费者

本章目标

本章解决 Kafka 日常开发最常见的问题:

  • Topic 应该怎么设计。
  • Partition 数量怎么估算。
  • Producer 如何保证顺序、吞吐和不丢。
  • Consumer 如何控制并发、提交 offset、处理失败。

Topic 设计

Topic 是事件类型边界,不是业务表的简单复制。一个好 topic 应该表达事件语义,而不是表达某个方法调用。

推荐命名:

命名 含义 说明
order-events 订单领域事件 包含 CREATED、PAID、CANCELLED
payment-events 支付领域事件 支付成功、失败、退款
user-behavior-events 用户行为事件 点击、曝光、搜索
inventory-commands 库存命令 如果明确是命令而不是事实事件

不推荐命名:

命名 问题
test 无业务语义,难治理
service-a-to-service-b 强耦合两个服务
order-table 容易把 Kafka 当数据库同步表
all-events Schema 混乱,权限和保留策略难控制

事件建模示例

订单创建事件建议带上事件 ID、业务 ID、事件类型、版本和发生时间:

json 复制代码
{
  "eventId": "EVT-20260502-0001",
  "eventType": "ORDER_CREATED",
  "eventVersion": 1,
  "occurredAt": "2026-05-02T20:30:00+08:00",
  "orderId": "O1001",
  "userId": "U1",
  "amount": 99.8,
  "status": "CREATED"
}

字段设计要点:

  • eventId:用于幂等处理和排查。
  • eventType:一个 topic 可以承载同一领域内多个事件类型。
  • eventVersion:用于兼容升级。
  • occurredAt:事件真实发生时间,不等于 Kafka 写入时间。
  • orderId:适合作为消息 key,保证同一订单进入同一 partition。

Partition 设计

Partition 决定并行度、吞吐、顺序边界和未来扩展成本。

Partition 数量估算

简单公式:

text 复制代码
partition_count = max(目标写入吞吐 / 单分区写入吞吐, 目标消费吞吐 / 单消费者吞吐)

例子:

  • 峰值写入:60 MB/s。
  • 单 partition 稳定写入:10 MB/s。
  • 单消费者处理:5 MB/s。
  • 目标消费者并行度:12。

则 topic 至少需要:

text 复制代码
max(60 / 10, 60 / 5) = max(6, 12) = 12 个 partition

实际生产中还要给未来增长留空间,例如设置为 18 或 24。

Partition 过多的问题

Partition 不是越多越好:

  • 文件句柄和日志段数量增加。
  • leader election 更慢。
  • Controller 元数据压力更大。
  • Consumer rebalance 时间变长。
  • 小流量 topic partition 过多会浪费资源。

消息 Key 与顺序

Kafka 只保证同一个 partition 内有序。生产者发送消息时,如果指定 key,默认分区器会根据 key 哈希选择 partition。

如果要保证同一订单的状态事件顺序:

text 复制代码
key = orderId
topic = order-events

这样 O1001CREATED -> PAID -> SHIPPED 会进入同一个 partition。

注意:如果后期增加 partition,同一个 key 的哈希结果可能变化,只能保证变更后新消息进入新规则下的 partition,不能保证跨变更的全局顺序连续。

Producer 核心配置

配置 推荐值 作用
acks all 等待 leader 和 ISR 副本确认,提高可靠性
enable.idempotence true 开启幂等生产,避免重试导致重复写入
retries 较大值 可恢复错误自动重试
delivery.timeout.ms 业务可接受范围 发送总超时时间
linger.ms 5-50 等待更多消息组成批次,提高吞吐
batch.size 32KB-128KB 起调 批次大小
compression.type lz4zstd 降低网络和磁盘压力

可靠生产者配置示例:

properties 复制代码
spring.kafka.producer.acks=all
spring.kafka.producer.retries=10
spring.kafka.producer.properties.enable.idempotence=true
spring.kafka.producer.properties.delivery.timeout.ms=120000
spring.kafka.producer.properties.linger.ms=10
spring.kafka.producer.properties.batch.size=65536
spring.kafka.producer.properties.compression.type=lz4

Consumer 核心配置

配置 推荐值 作用
enable.auto.commit false 手动提交,避免处理失败但 offset 已提交
auto.offset.reset earliestlatest 无 offset 时从哪里开始读
max.poll.records 业务处理能力内 单次拉取数量
max.poll.interval.ms 大于单批最大处理时间 防止处理慢被踢出消费组
session.timeout.ms 10-30s 心跳会话超时
partition.assignment.strategy cooperative sticky 降低再均衡影响

手动提交的基本原则:

text 复制代码
拉取消息 -> 执行业务处理 -> 业务处理成功 -> 提交 offset

如果顺序反了:

text 复制代码
拉取消息 -> 提交 offset -> 执行业务处理

业务处理失败时,Kafka 会认为消息已经处理完,造成业务丢失。

消费失败处理策略

策略 适用场景 风险
立即重试 网络抖动、临时数据库失败 可能阻塞 partition
有限次重试 大多数业务异常 需要记录失败原因
死信队列 DLT 无法处理的脏数据 需要补偿流程
跳过并告警 非核心日志类消息 可能丢业务语义
暂停消费 下游故障、避免雪崩 堆积增长,需要容量预案

Spring Kafka 中常用 DefaultErrorHandler 配合 DeadLetterPublishingRecoverer。本包项目 demo 已实现失败重试和 DLT。

再均衡 Rebalance

Rebalance 是消费组内 partition 分配关系发生变化的过程。触发场景:

  • 消费者实例新增或退出。
  • 消费者心跳超时。
  • topic partition 数量变化。
  • 订阅 topic 变化。

Rebalance 期间,部分 partition 会暂停消费。生产环境要降低频繁 rebalance:

  • 处理逻辑不要阻塞太久。
  • 合理设置 max.poll.interval.ms
  • 使用静态成员 ID:group.instance.id
  • 使用 cooperative sticky 分配策略。
  • 优雅停机,让消费者主动退出。

实操:观察消费组和分区分配

创建 3 分区 topic:

bash 复制代码
docker compose exec kafka kafka-topics \
  --bootstrap-server localhost:9092 \
  --create \
  --if-not-exists \
  --topic partition-lab \
  --partitions 3 \
  --replication-factor 1

启动第一个消费者:

bash 复制代码
docker compose exec kafka kafka-console-consumer \
  --bootstrap-server localhost:9092 \
  --topic partition-lab \
  --group partition-group \
  --property print.partition=true \
  --property print.offset=true

再打开第二个终端启动第二个消费者,观察两个消费者分配 partition 的变化:

bash 复制代码
docker compose exec kafka kafka-console-consumer \
  --bootstrap-server localhost:9092 \
  --topic partition-lab \
  --group partition-group \
  --property print.partition=true \
  --property print.offset=true

发送带 key 的消息:

bash 复制代码
docker compose exec kafka kafka-console-producer \
  --bootstrap-server localhost:9092 \
  --topic partition-lab \
  --property parse.key=true \
  --property key.separator=:

输入:

text 复制代码
O1001:created
O1001:paid
O2001:created
O2001:paid

验证点:相同 key 通常进入同一个 partition,同一个 partition 内 offset 递增。

相关推荐
空中海3 小时前
Kafka Streams、Connect 与生态
分布式·kafka·linq
KmSH8umpK17 小时前
Redis分布式锁从原生手写到Redisson高阶落地,附线上死锁复盘优化方案进阶第三篇
redis·分布式·wpf
KmSH8umpK20 小时前
SpringBoot 分布式锁实战:从单机锁到Redis分布式锁全覆盖,解决超卖、重复下单、幂等并发问题
spring boot·redis·分布式
KmSH8umpK1 天前
Redis分布式锁从原生手写到Redisson高阶落地,附线上死锁复盘优化方案
redis·分布式·wpf
长河1 天前
XXL-JOB 从本地快速上手到核心架构深度解析
分布式
juniperhan1 天前
Flink 系列第22篇:Flink SQL 参数配置与性能调优指南:从 Checkpoint 到聚合优化
大数据·数据仓库·分布式·sql·flink
juniperhan1 天前
Flink 系列第21篇:Flink SQL 函数与 UDF 全解读:类型推导、开发要点与 Module 扩展
java·大数据·数据仓库·分布式·sql·flink
marsh02062 天前
41 openclaw分布式会话管理:跨服务状态同步方案
分布式·ai·编程·技术
杰建云1672 天前
Plurai 分布式推理引擎深度评测
分布式