前置知识: Git

消息队列概述与选型

7 min入门

零基础第一课:消息队列解决什么问题、三种投递语义、Kafka/RabbitMQ/Pulsar 对比与选型。

0. 一句话理解

消息队列(MQ)就是”生产者把消息放进信箱,消费者有空再取”:它让系统之间解耦、削峰、异步,是分布式系统的标配中间件。

1. 消息队列解决什么问题

1.1 解耦

下单后要通知库存、积分、短信三个系统。直接调用接口,每加一个系统就要改代码;改成”下单消息发到队列”,新系统自己订阅即可。

1.2 削峰

秒杀瞬间 10 万请求打到数据库会挂。先把请求写进队列,消费端按数据库能承受的速度慢慢处理——“排队打饭”而不是”一起挤窗口”。

1.3 异步

发邮件、生成报表等耗时操作从请求链路里挪到后台异步执行,接口响应从 3 秒降到 100 毫秒。

1.4 MQ 在一次下单链路中的位置

publish 订单事件

订阅

订阅

订阅

用户下单

订单服务

只写订单库+发一条消息

消息队列

库存服务

积分服务

短信服务

publish 订单事件

订阅

订阅

订阅

用户下单

订单服务

只写订单库+发一条消息

消息队列

库存服务

积分服务

短信服务

mermaid diagram

对比同步调用:订单服务不再知道库存/积分/短信的存在,任何下游挂了都不影响下单, 新下游上线只需要”多一个订阅者”。

2. 核心概念

概念说明生活类比
Producer 生产者发消息的一方寄信人
Consumer 消费者收消息处理的一方收信人
Broker 代理消息暂存与转发的服务器邮局
Topic / Queue消息的分类容器信箱
消费组一组消费者分担消息多个邮递员分工

3. 三种投递语义

语义含义代价
At-most-once 至多一次消息可能丢,但绝不重复最低
At-least-once 至少一次不丢,但可能重复需消费端幂等
Exactly-once 精确一次不丢不重最高,通常配合事务/幂等实现

企业默认选择 At-least-once + 消费端幂等:既不丢消息,又用”去重表/唯一键”把重复消费变成无害操作。

4. 主流中间件对比

维度KafkaRabbitMQPulsar
定位分布式事件流平台传统消息代理云原生流平台
模型Topic + 分区Exchange + QueueTopic + 分区
顺序保证分区内有序单队列有序分区内有序
吞吐极高中高
延迟毫秒级微秒级毫秒级
重放消息可保留重读消费后删除可保留重读
典型场景日志、指标、事件流任务分发、RPC 解耦混合场景、多租户

5. 什么时候不要用消息队列

  • 调用方必须立刻知道结果(改用同步 API);
  • 系统只有一个模块、没有峰值压力(引入 MQ 徒增复杂度);
  • 需要强事务跨系统一致性(MQ 只能最终一致,账务场景要慎重)。

6. 推模式与拉模式:消费端的两条路线

维度推(Push):RabbitMQ拉(Pull):Kafka
触发方式Broker 主动送上门消费者主动来取
优点低延迟,消费端简单消费端按能力取,天然背压
缺点需要 prefetch 限流防”撑死”有轮询延迟,长轮询优化后毫秒级
类比食堂阿姨帮你打菜自己端着餐盘去窗口

Kafka 的”长轮询”(long polling)让拉模式在没消息时也不空转——这点知道即可, 重要的是记住:RabbitMQ 的积压治理靠 prefetch,Kafka 的积压治理靠消费组扩容。

7. 选型决策树

是

否,是任务分发/复杂路由

是

否,量不大

是

否

是

否

云原生多租户/存算分离

需要的是日志/事件流

还是要回放历史?

吞吐是海量级

或要多消费组各自读全量?

需要灵活路由规则

与低延迟?

Kafka

团队已熟某套技术栈?

沿用现有即可

别为选型而选型

RabbitMQ 也完全够用

Pulsar

是

否,是任务分发/复杂路由

是

否,量不大

是

否

是

否

云原生多租户/存算分离

需要的是日志/事件流

还是要回放历史?

吞吐是海量级

或要多消费组各自读全量?

需要灵活路由规则

与低延迟?

Kafka

团队已熟某套技术栈?

沿用现有即可

别为选型而选型

RabbitMQ 也完全够用

Pulsar

mermaid diagram

三个常识校准:

  1. 量不大就别上 Kafka:Kafka 的运维成本(分区、副本、控制器、滚动升级)显著高于 RabbitMQ,日均百万级以下的消息 RabbitMQ 更省心。
  2. Kafka 消息默认保留 7 天可重放,RabbitMQ 消费确认后即删——这是两种世界观, 决定了”历史回放”能力只有流平台才有。
  3. 云厂商托管版优先于自建:自建 MQ 集群是运维深坑,除非有特殊合规要求, 优先评估托管服务(MSK/Amazon MQ/阿里云 RocketMQ 等)。

8. 常见误区

误区现实
“MQ 能保证消息绝对不丢”默认配置都可能丢(发送无确认、无持久化);不丢是靠整套机制堆出来的,见可靠消息篇
“Exactly-once 开箱即用”各家实现都限定范围(如 Kafka 事务只在 Kafka Streams/事务 API 内有效),跨系统仍要靠消费端幂等
“加了 MQ 系统就解耦了”消息格式成了隐式契约,不治理 schema 就是新的强耦合
“消息队列 = 降级仓库,堆着慢慢消费”积压是有成本的:内存/磁盘、消息过期、时效性承诺全部受损,积压必须监控与治理

9. 实践:动手与自检

判断题(5 分钟):下列三个场景,哪些适合引入 MQ?(a)支付回调后通知积分系统发分; (b)网关查询用户余额决定放行与否;(c)每晚批量生成报表。验收:能对每个场景说出 「调用方是否关心结果」「是否有峰值」两条判断依据。

提示(思路方向):判断标准就是第 1 节的三大价值——解耦、削峰、异步;反过来第 5 节 「什么时候不要用」是排除法。参考答案:a 适合(通知类,调用方不关心结果,异步即可); b 不适合(同步链路必须立刻知道结果,引 MQ 纯增延迟);c 不适合(无并发压力,定时任务 直接做更简单)。

观察题(15 分钟,衔接后两章):分别用 Docker 启动 Kafka 与 RabbitMQ(命令见后两章), 各跑通「发一条、收一条」,然后对照本篇填一张表:消息消费后还能再读到吗?消费者挂了 未确认的消息去哪了?验收:能用自己的话复述「可重放」与「消费即删」两种世界观的差异。

提示:Kafka 用 --from-beginning 重新消费验证可重放;RabbitMQ 观察管理后台队列深度 归零。两边跑完再回看第 4 节对比表,每一行都应该有了体感。

设计题(20 分钟):把第 1.4 节的下单链路画成时序图(Mermaid sequenceDiagram),标出 每一步的失败后果:消息发送失败会怎样?库存服务消费失败会怎样?验收:图里能看出 「订单库已写入但消息没发出去」这个不一致窗口——它就是可靠消息篇本地消息表要解决的问题。

提示(思路方向):先画正常路径,再对每一步加「失败分支」。先自己画,再对照骨架:

库存服务消息队列订单服务用户库存服务消息队列订单服务用户下单写订单库(事务内)发送订单事件(若这里失败:库有单、下游永远不知道)投递(若消费失败:需重试与死信兜底)扣库存
库存服务消息队列订单服务用户库存服务消息队列订单服务用户下单写订单库(事务内)发送订单事件(若这里失败:库有单、下游永远不知道)投递(若消费失败:需重试与死信兜底)扣库存
mermaid diagram

复盘题(5 分钟):回想一次你遇到的「下游系统拖垮上游」或「峰值打挂数据库」的事故 (没有就虚构一个秒杀场景),用本篇的语言重新诊断:事故链里哪一步本该由 MQ 承接? 验收:诊断里至少用到「解耦、削峰、异步」中的一个词,且说清为什么用 MQ 能切断事故链。

提示:削峰对应「打挂」类事故——请求先落队列,消费端按数据库承受速度处理;解耦对应 「拖垮」类事故——下游故障时上游只管发消息,不被同步调用卡死。想不清时回到第 1.2 节 的「排队打饭」类比检查推理。

10. 一句话记住

MQ 的三大价值是解耦、削峰、异步;选型看场景:海量事件流选 Kafka,灵活任务分发选 RabbitMQ,云原生多租户选 Pulsar;投递语义默认 At-least-once + 消费端幂等。