CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Kafka 消息队列入门/课时/消息队列是什么
RawBlame
课时目录12 节

Kafka 消息队列入门

  1. 01消息队列是什么
  2. 02核心概念
  3. 03Docker 运行 Kafka
  4. 04生产者
  5. 05消费者组
  6. 06Spring Kafka
  7. 07消息格式
  8. 08投递语义
  9. 09Kafka Streams 入门
  10. 10监控与 lag
  11. 11Kafka vs RabbitMQ
  12. 12综合练习

消息队列是什么

同步 vs 异步;与 Redis Pub/Sub 区别。

30 分钟

消息队列是什么

这一课你将能做到

  1. 画出「同步 HTTP 调用」与「异步发事件」两条链路,指出用户等待边界各落在哪一步
  2. 用 Topic 和 Consumer Group 两个词解释:三个下游服务如何各自完整消费同一批订单事件
  3. 对「缓存失效广播 / 订单多下游处理 / 改昵称」三类场景,判断该用 Redis、Kafka 还是只写 MySQL,并各写一句理由

为什么要学

下单后要发通知、加积分、同步搜索索引。若在下单接口里串行 HTTP 调用三个服务,任何一个慢了或挂了,整笔订单接口就被拖垮:用户盯着转圈,接口 P99 飙升,Nginx 上的超时和重试开始堆积。

消息队列(Message Queue,MQ)把「发生了什么」写成一条消息交给中间件,下游各服务按自己的节奏处理。用户只等「订单写入 MySQL 成功」,发通知、加积分这些事后消化。真实开发里它解决三件事:

  1. 解耦:一次动作多个下游要响应,服务之间不再互相依赖对方的部署与可用性
  2. 削峰:秒杀、批量导入的写入尖峰先落进日志,下游按自身能力匀速消费
  3. 可回溯:事件按 offset 保留一段时间,出了问题能重放历史修数据

面试层面,MQ 是 Java 后端必考题:为什么用 MQ、引入 MQ 的代价(重复消费、消息丢失、顺序性)、Kafka 与 RabbitMQ 怎么选。本 track 后续 11 课会把这些问题逐个变成你能动手验证的实验。

人话:MQ 是服务之间的「留言板」——写的人写完就走,读的人稍后自己来取,双方互不等待。

先建立图像

同步(紧耦合):
  用户 → 下单 API ──HTTP──► 库存服务
                 ──HTTP──► 通知服务
                 ──HTTP──► 积分服务
  任一慢/挂 → 用户一直等,整单失败

异步(解耦):
  用户 → 下单 API → MySQL 事务提交 → 发 OrderCreated 到 Topic
                                        │
                     ┌──────────────────┼──────────────────┐
                     ▼                  ▼                  ▼
                通知服务(组A)       积分服务(组B)        搜索服务(组C)
                各组各自记 offset,互不抢对方进度

三种「传话」方式对比:

需求Redis Pub/SubKafka Topic
消费者离线后再补历史❌ 当时不在线就丢✅ 日志按 offset 回溯
多实例竞争分摊❌ 广播给所有订阅者✅ 同一 Consumer Group 分摊分区
高吞吐事件流轻量通知够用设计目标就是追加写日志
本站典型用途缓存失效广播订单/审计/跨服务事件
[spring-boot API] --produce--> Topic: order-events (多分区)
                                     │
              Consumer Group notify  │  Consumer Group audit
              (组内互斥分摊)          │  (另一组完整再读一遍)

跟着做

步骤 1:钉死「用户等什么」

打开笔记写两行:

  1. 同步:用户等到「库存 + 通知 + 积分」全部返回才拿到 200
  2. 异步:用户等到「订单行写入 MySQL 成功」就拿到 200,通知由后台消费发出

结论写一句话:异步不是「不做通知」,而是把通知移出请求临界路径。

步骤 2:画用户注册的两条时序

用纸或 mermaid 画「用户注册」:

  • 同步:API → 写用户表 → HTTP 发欢迎邮件 → HTTP 记埋点统计
  • 异步:API → 写用户表 → produce UserRegistered → 邮件组 / 统计组各自消费

异步图上必须标出 Topic 名(如 user-events)与两个不同的 group.id。应看到:同一 Topic、两个组 = 两条独立订阅线,不是「抢一条消息」。

步骤 3:用代码体会「异步边界」

先不碰 Kafka,在任意 Spring Boot 项目里模拟:

@PostMapping("/sync-order")
public String syncOrder() {
    try {
        Thread.sleep(2000); // 模拟调用通知服务慢
        Thread.sleep(1000); // 模拟调用积分服务慢
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    return "ok"; // 用户等了 3 秒才拿到响应
}

把这个接口改成「写库成功即返回,其余交给后台消费者」,你就完成了本课最重要的判断:哪些步骤属于用户临界路径,哪些属于事后处理。判断口诀——这步失败用户能否接受稍后完成或人工补偿?能 → 异步;不能(如扣款、扣库存)→ 同步事务内做。

步骤 4:读官方定位一段

打开 Apache Kafka Introduction,用中文写一句:Kafka 是分布式事件流平台,把记录写成可分区、可复制的提交日志。再补半句本课立场:订单状态以 MySQL 为准,Kafka 存的是事件流副本。

步骤 5:场景分流三连

对每个场景写「Redis / Kafka / 只 MySQL」+ 一句理由:

  • 课程详情页缓存过期后的失效广播
  • 订单创建后通知、审计、搜索三方都要处理
  • 用户改昵称写 cf_user

参考方向:失效广播偏 Redis(瞬时、丢了无所谓);订单多下游偏 Kafka(要历史、要多订阅者);改昵称权威写 MySQL(单一真相源)。

步骤 6:看一条事件长什么样

{
  "event": "OrderCreated",
  "eventId": "01JBEXAMPLEUUID",
  "orderId": "1001",
  "userId": "u42",
  "amount": 9900,
  "occurredAt": "2026-10-06T00:00:00Z"
}

消息是「发生了什么」的快照,不是订单表本身;字段设计下一阶段的「消息格式」课展开。

讲清楚要点

  1. MQ 的本质是「把时间上必须紧挨着的两件事拆开」:生产方只负责把事件写进日志,消费方按自己的节奏读。拆开后两侧可以独立扩容、独立失败、独立部署。
  2. Kafka 的消息是「读不删」的日志:与队列「领走即删」不同,消息保留在分区里一段时间,多个消费组可以各自从头读——这决定了它天然适合多订阅者与重放。
  3. 用户等待边界由你定义:临界路径内只放「必须同步完成」的事(事务写库),其余进事件流。边界画错,要么接口变慢,要么业务丢步骤。
  4. 组内互斥分摊、组间完整重读:通知组与审计组用不同 group.id,一条消息两边各处理一次;这跟「两个服务抢一条消息」是两回事。
  5. 业务真相仍在 MySQL:消息发送成功 ≠ 业务成功。Kafka 存事件副本,修复下游靠重放,改账仍走数据库事务。
  6. 不要为了「架构先进」上 MQ:小流量单体用同步 REST + 事务最简单;MQ 一旦引入,重复、乱序、积压、监控都是新增成本,必须换得回收益。

你可能会懵的地方

  1. 「异步 = 不可靠?」 不是。可靠性靠 Producer acks、重试、消费幂等与 lag 监控(后续课逐个落实)。用户看到的「成功」应对齐数据库提交,通知属于最终一致——延迟通常毫秒到秒级。

  2. 「一个 Topic 多个服务会抢消息吗?」 同一组内才会互斥分摊;不同 group.id 各有独立 offset,同一条事件可以被通知组与审计组各读一次。

  3. 「有了 Redis 还要 Kafka?」 Redis Pub/Sub 适合瞬时通知(缓存失效),不落历史;Kafka 适合要历史、要分摊、要多订阅者的事件流。两者职责不同,不是替代关系。

  4. 「微服务必须上 Kafka?」 不是。小团队同步 REST/Feign 完全够;出现多下游、流量尖峰、需要重放时再上,收益才盖过运维成本。

  5. 「消息丢了怎么办?」 先问丢在哪一段:生产侧(acks 配置)、Broker 侧(副本与刷盘)、消费侧(提交时机)。三段各有对策,本 track 的「生产者」「投递语义」「监控」三课会逐段拆解。

错法对照

错法问题正解
下单接口里同步调通知/积分/搜索,任一超时整单失败下游故障被打进用户临界路径事务写订单成功后发 OrderCreated,下游独立消费
用 Redis Pub/Sub 存订单事件,期望离线消费者补数据Pub/Sub 无历史,错过即丢订单类事件进 Kafka,按 offset 回溯
两个服务共用一个 group.id 却期望各收全量变成竞争消费,互相抢分区通知与审计用不同 group.id
认为「Kafka 发送成功 = 下单成功」Broker 确认只代表写入日志MySQL 事务成功才算业务成功,消息是旁路事件
为「架构完整」给单体站点强行上 Kafka运维成本远超收益主路径 MySQL + Redis,Kafka 作为扩展实验

卡点急救

  1. 分不清「什么该异步」:拿业务步骤逐条问「这步失败,用户能否接受稍后完成或人工补偿?」能 → 异步;不能(扣款、扣库存、发凭证)→ 留在同步事务内。
  2. 担心消息丢导致业务断链:先做「对账兜底」——定时任务扫订单表,把「已支付但未发通知」的行补发事件;消息系统是加速器,对账是安全网,两者都要有。
  3. 团队没人用过 Kafka 不敢上:先按下一课的 Docker 方案把链路跑通(约 10 分钟),用实验数据支撑选型争论,比空对空讨论架构图有效得多。

面试常问

  • 为什么要用消息队列? 背三个词:解耦(生产者不依赖消费者存活)、异步(下游耗时移出用户临界路径)、削峰(尖峰先入日志,下游匀速消费)。同时主动补一句代价:引入重复消费、消息丢失、顺序性、可用性四大问题,需要幂等与监控配套——能说出代价才显得真用过。

  • 用了消息队列会有什么问题? ①重复消费:先处理再提交位点的 at-least-once 模型必然可能重复,靠业务幂等(唯一键/去重表)解决;②消息丢失:生产 acks、Broker 副本、消费提交时机三段都可能丢,各有配置对策;③顺序性:Kafka 只保证分区内有序,需按实体 key 路由;④复杂度:多一个需要监控(lag)与运维的组件。

  • 为什么选 Kafka 而不是 RabbitMQ? 答需求形状而不是品牌:多订阅者、要重放历史、吞吐量大(日志追加、顺序写磁盘)选 Kafka;任务队列、灵活路由、延迟重试、死信开箱即用选 RabbitMQ;轻量瞬时通知用 Redis Pub/Sub。

自测三问

  1. 用户注册成功后要写库、发欢迎邮件、记埋点:画出同步与异步各 3 步,异步图上标 Topic 与两个 group。
  2. 缓存失效广播用 Redis、订单多下游用 Kafka:各写一条「不可替代」的理由。
  3. 同一 Topic 上 notify 与 audit 两个组同时跑,一条消息会被处理几次?谁决定「组内是否重复」?

小结一句话

消息队列把「紧挨着的两件事」拆成「写日志 + 读日志」:解耦、削峰、可重放;Redis 管轻通知,Kafka 管事件流,业务真相仍在 MySQL。

延伸

  • Apache Kafka 官方介绍
  • 下一课「核心概念」:Broker、Topic、Partition、Offset、Consumer Group 五个词是后面每一课的地基,一次钉死。
加载本节练习…
下一课 →核心概念