你是小阿巴,刚刚为电商系统的双 11 大促开发了秒杀抢购功能。
0 点秒杀开始,每秒上万个用户同时点击抢购按钮,你的数据库瞬间被打垮!

你急得满头大汗,只能找到 "后端之狗" 鱼皮求助:阿巴阿巴......
鱼皮看了看你的代码:用户每次抢购,你的代码都要同步处理校验库存、扣减库存并创建订单等多个操作。同时抢购的用户多了,系统就会压力山大。

你急了:那咋办啊?我总不能拒绝用户吧!
鱼皮嘿嘿一笑:可以用消息队列呀。
你一脸懵:消息队列?那是啥?
⭐️ 推荐观看视频讲解,更通俗易懂:bilibili.com/video/BV12q...
第一阶段:认识消息队列
鱼皮:消息队列(俗称 MQ)就像一个快递驿站。快递员作为 生产者 ,把包裹(消息)放到驿站(消息队列),收件人作为 消费者,自己到驿站去取。
这样一来,快递员放下包裹就能走,不用费时间等你签收(异步)。

你也不用关心是谁送的、不用和快递员见面,有空去驿站取就行(解耦)。

就算双 11 包裹很多,驿站也能暂存起来等消费者慢慢取(削峰)。

这就是消息队列的三大作用:异步、解耦和削峰。
对于你的秒杀抢购系统,现在用户每次抢购都要同步执行各种操作(校验库存、扣减库存、创建订单),全部完成才能返回结果。

如果使用消息队列,用户点击抢购后,系统先做基本的校验并在缓存中预扣库存,确认可以抢购后,把抢购请求(消息)投入消息队列,就可以立刻返回结果了(页面)。

接下来由后台服务从队列中取出消息并处理那些耗时的操作,比如创建订单、在数据库中真正扣减库存。
这就是 异步:用户不用等所有后端操作完成,就能快速得到响应。

而且,抢购服务只管发消息,不用关心是谁来处理库存、谁来创建订单。假如以后要加短信通知、用户行为分析等功能,只需要让新服务也去监听消息队列就行,完全不用修改抢购服务的代码。
这就是 解耦:服务之间不直接依赖,让系统能灵活扩展。

如果同时抢购的用户过多,数据库处理不过来,也可以用消息队列做缓冲。所有的抢购请求先快速存入队列,后台服务可以根据自己的能力从队列中慢慢拉取消息并进行处理。

这就是 削峰:削平流量洪峰,保护后端系统不被压垮。

你恍然大悟:原来如此,消息队列也太强大了吧!俺要学,俺要学!
鱼皮:很有干劲嘛!主流的消息队列实现技术有:
- RabbitMQ:简单易学、生态活跃,适合中小规模应用
- Kafka:吞吐量高、延迟极低,适合大数据、日志收集场景
- RocketMQ:阿里出品、支持事务消息,适合电商金融等对可靠性要求高的场景
你:阿巴阿巴,这么多都要学吗?
鱼皮:新手建议从 RabbitMQ 开始入门,学好一个再学其他的就很简单了。下面我就以 RabbitMQ 为例带你快速掌握消息队列必知必会的技术。

你:期待住了,这不得点赞狠狠支持?!

第二阶段:RabbitMQ 快速上手
鱼皮:首先来安装 RabbitMQ,笨办法是先安装 Erlang 语言环境,再去 官网下载 MQ 的安装包并执行。

但是更建议直接用 Docker 容器技术,快速安装和运行,不用担心版本不兼容。

安装完之后,访问 http://localhost:15672 就可以打开 RabbitMQ 内置的可视化管理界面(默认用户名和密码都是 guest),你可以在这里查看队列的运行情况、手动发送和接收消息。

另外,RabbitMQ 也提供了命令行工具 rabbitmqctl,主要用于运维管理,比如创建用户、查看状态等。

你:那我怎么用代码操作 RabbitMQ 呢?
鱼皮:RabbitMQ 提供了各种编程语言的 SDK 开发包,如果你是 Java 开发者,推荐使用 Spring AMQP。
AMQP 是高级消息队列协议(Advanced Message Queuing Protocol)的缩写,是一个开放标准,不绑定特定技术。RabbitMQ 就是基于 AMQP 实现的,所以客户端库叫 Spring AMQP。
只需几行代码,就能创建队列、让生产者发送消息、让消费者接收处理消息:

你:不是吧,这么简单?!
鱼皮:没错,但刚刚我们只是完成了最简单的 1 对 1 发送和接收消息。
实际上 RabbitMQ 有 6 种工作模式,适用于不同的业务场景,这也是消息队列的学习重点。
第三阶段:RabbitMQ 工作模式
简单模式
最简单的是 Simple 模式,一个生产者、一个队列、一个消费者。
就像老板派发任务给员工,队列(Queue) 是存储任务的容器,老板把任务放进去,员工从里面取出来完成。

工作队列模式
鱼皮:如果有很多任务要处理,一个员工忙不过来怎么办?
你:多找几个员工帮忙?
鱼皮:没错,这就是 Work Queue 工作队列模式,一个生产者、一个队列、多个消费者。
就像老板发布了一堆任务,RabbitMQ 会把任务依次分配给员工,但是一个任务只会被一个员工完成。

发布订阅模式
你:这样效率就高多了!
但如果老板要求所有员工都写工作总结,怎么把同样的任务发给多个员工呢?

鱼皮:好问题!之前工作队列是多个员工共享一个任务列表,而现在每个员工都要有自己的任务队列。
老板需要利用 交换机(Exchange) 来控制把任务分发给哪些和它绑定的队列。
比如想把任务发给所有员工,就要用到 广播交换机(Fanout Exchange) ,它会把任务发给所有已绑定的队列,然后每个员工分别从自己的队列取任务并完成。这就是 发布订阅模式(Publish/Subscribe) 。

路由模式
你:那如果老板想把某些任务发给特定员工呢?比如鱼皮负责写代码和修 Bug,小阿巴负责写代码和摸鱼~
鱼皮:可以使用 路由模式(Routing) ,给每个员工的队列设置自己负责的路由键(Routing Key):
- 鱼皮的队列绑定 "写代码" 和 "修 Bug"
- 小阿巴的队列绑定 "写代码" 和 "摸鱼"
老板发布任务时会指定路由键,由 直接交换机(Direct Exchange) 根据路由键精确匹配,把任务发给对应的队列。

主题模式
你:那如果老板想把所有前端相关的任务都发给前端员工,后端相关的任务都发给后端员工呢?一个一个指定路由键是不是太麻烦了?
鱼皮:可以使用 主题模式(Topic) ,它使用 主题交换机(Topic Exchange) ,支持使用通配符模糊匹配。
可以给队列绑定通配符路由键,就能接收所有匹配的任务:
- 前端员工的队列绑定
frontend.*(匹配 1 个词),能匹配frontend.Vue、frontend.React - 后端员工的队列绑定
backend.#(匹配多个词),能匹配backend、backend.Java、backend.Java.优化

你:哇,这样一来就灵活多了!
鱼皮:没错,这 5 种模式是企业中最常用的,掌握了它们就能应对大部分场景了。至于最后一种模式 ------ RPC(远程过程调用)模式,暂时不需要了解,因为企业开发中一般用专门的 RPC 框架,比如 gRPC、Dubbo。

你:好嘞,我先用这些工作模式来重构秒杀抢购功能,请好吧您嘞!
第四阶段:消息队列生产实践
一个月后,你意气风发地找到鱼皮:鱼皮 gie,我的抢购代码重构完了,快帮我看看能不能上线~
鱼皮看着你的代码,表情难受得像持矢一样:emmm,你这代码要是上线,公司就完蛋了!

你:阿巴阿巴,我本地测试过了,完全没问题啊!
鱼皮:消息队列在生产环境中的坑可多着呢!下面我来问你几个问题。
第一问:消息会丢吗?
鱼皮:如果重启 RabbitMQ 服务器,队列里的消息会丢吗?
你支支吾吾:会......会丢吧?
鱼皮:当然会!在抢购系统中,如果消息丢了,订单永远不会被创建,用户那边会一直显示 "抢购中"。

你着急了:那咋办啊,我会吃投诉的!
鱼皮:要保证消息不丢失,需要做好持久化 3 件套,把数据从内存保存到硬盘:
- 队列持久化,创建队列时设置 durable 为 true。
- 消息持久化,发送消息时设置消息为持久化模式(比如 deliveryMode = 2 或 persistent = true)
- 交换机持久化,创建交换机时设置 durable 为 true。

你:哈哈,这下消息就不会丢了吧!
鱼皮:不一定!光持久化还不够,还要有消息确认机制来保证消息的可靠性。
1)生产者确认(Publisher Confirm):生产者发送消息后,等待 RabbitMQ 的确认回复,确保消息真的被接收了。就像寄快递,你把包裹交给快递员,快递员要给你一个回执单,证明他收到了。

2)消费者确认(Consumer ACK):消费者处理消息成功后,要手动告诉 RabbitMQ "我处理完了,你可以删了"。

千万别用默认的自动确认,那样消息一收到就删除,万一处理失败消息就丢了。就像收快递要签收,确保包裹真的送到你手上了。
鱼皮:这样从生产到消费的整个链路都有保障,消息就不会丢失了。

第二问:消息会重复吗?
鱼皮:如果网络抖动,或者消费者重启,同一条消息可能被消费多次。比如抢购消息被重复消费,同一个用户的订单被创建了 2 次,库存被扣了 2 次怎么办?

你:那我得跑路了!
鱼皮:不至于不至于,我们要保证 消息幂等性,让重复消费等同于只消费一次。
常见的方案有 3 种:
1)给每条消息一个唯一 ID(比如 UUID 或雪花算法 ID),消费前先检查这个 ID 是否处理过。
2)利用数据库唯一索引,比如订单号设置为唯一索引,重复插入会失败。

3)使用 Redis 分布式锁,同一条消息同一时间只能有一个消费者处理。

你:明白了!抢购时为了防止重复下单,要检查用户是否已经下过单了。

鱼皮点头:没错,幂等性设计是分布式系统的基本功。
第三问:消息乱序怎么办?
鱼皮:比如用户先抢购下单、然后取消订单,分别向队列发了 "创建订单" 和 "取消订单" 2 条消息。如果处理顺序乱了,系统先处理取消、后处理创建,订单状态就错了。

你:队列不是先进先出的吗?RabbitMQ 应该天然有序吧?
鱼皮:单队列内确实有序,但如果你开了多个消费者并发处理,可能消息 1 还在处理,消息 2 已经处理完了,顺序就乱了。

你:那怎么保证顺序呢?
鱼皮:在 RabbitMQ 中,要严格保证顺序,建议用 单队列 + 单消费者。
你:那性能不就很低了吗?
鱼皮:没错,所以要根据业务需求权衡一致性和性能。
如果你全都要,可以考虑 Kafka 的分区机制,可以将同一用户的消息路由到同一个分区,同一个分区内的消息严格有序,不同用户的消息又可以并发处理。

你:妙啊,又学到一个架构知识!
第四问:消息处理失败怎么办?
鱼皮:如果消费者处理消息时出错了,比如数据库连接失败、业务逻辑异常,怎么办呢?

你:重试?
鱼皮:重试几次还是失败呢?
你:阿巴阿巴,直接删掉消息?
鱼皮:删掉不就丢了吗?!这时应该要用死信队列(Dead Letter Queue)。
3 种情况会产生死信:
- 消息被消费者拒绝(reject 或 nack,requeue 参数设置为 false)
- 消息过期
- 队列满了

你:呜呜呜,这些死信好可怜。
鱼皮:没错,我们要给死信一个去处。可以配置死信交换机(DLX),将死信自动路由到死信队列。由专门的消费者监控死信队列,发现死信后,可以重试、告警、或者人工处理。

你:好耶,这样异常消息就不会丢失啦!
第五问:RabbitMQ 服务挂了怎么办?
鱼皮:前面说了这么多保证消息不丢的机制,但如果 RabbitMQ 服务本身挂了,整个系统就不能收发消息了,这可是大事故!
你汗流浃背了:那怎么办?
这时,练习两年的实习生阿坤突然鸡叫起来:我知道,要搭建集群和高可用架构!

生产环境至少要搭建 3 个 RabbitMQ 节点的集群。
对于重要的队列,要使用 Quorum 仲裁队列,它基于 Raft 协议实现,会把数据自动同步到多个节点,保证数据一致性。

如果主节点挂了,Raft 会自动选举新的主节点,实现故障转移,用户毫无感知。

如果数据量太大,单个队列存不下,可以使用 Sharding 插件创建 分片队列,把消息分散到多个节点。不过 RabbitMQ 并不擅长这个场景,建议使用 Kafka。

鱼皮拍了拍阿坤的肩膀:不错!生产环境的高可用是非常重要的。
此外,RabbitMQ 还有一些高级特性,在特定场景下很有用。
RabbitMQ 高级特性
1)延迟队列
比如想要自动取消超过 15 分钟还未支付的订单,可以用死信队列配合生存时间(TTL)实现。发消息时设置 TTL 为 15 分钟,等它过期变成死信,自动进入死信队列,然后让死信队列的消费者处理取消逻辑。但是更推荐使用专门的延迟插件 rabbitmq_delayed_message_exchange。
2)优先级队列
比如想优先处理 VIP 用户的订单,可以设置队列的最大优先级(x-max-priority),然后在发送消息时指定优先级,优先级高的消息会被优先消费。
3)Stream 流式存储
传统队列消息消费后就删除了,而 Stream 可以保留历史消息、重复消费、回溯历史,类似 Kafka,适用于实时数据分析、审计日志等场景。但我建议了解即可,不如用 Kafka。

你羞愧地低下了头:我以为自己已经掌握了 RabbitMQ,原来只是学了个皮毛...
第五阶段:深入原理
被鱼皮连环拷问后,你主动找到阿坤:坤哥你好强,我想深入学习 RabbitMQ 底层原理,请问你是咋学的啊?
阿坤有些惊讶:咦?你不背八股文的么?刷刷题 就好了呀!

你震惊了:现在的校招生,竟然恐怖如斯!
鱼皮:阿坤你别逗他了,其实可以带着问题学习,重点探索 RabbitMQ 的消息路由机制、队列存储结构、AMQP 协议和消息持久化的实现。
比如:
- 消息路由机制:Exchange 用什么算法匹配?Binding 如何存储?
- 队列存储结构:消息在内存还是磁盘?不同队列类型有什么区别?
- AMQP 协议:客户端和服务端怎么通信?数据格式是什么样的?
- 持久化实现:数据怎么写入磁盘?如何保证不丢失?

你好奇:那要怎么学习这些原理呢?
鱼皮:从这些问题出发,去阅读相关的文章,推荐官方文档和官方博客。

或者像阿坤说的刷一刷 RabbitMQ 面试题,就能快速学会很多核心知识点。

如果想增加求职竞争力,最重要的是做实战项目,我在 编程导航 上的的智能 BI 项目和 OJ 判题系统项目都有完整的 RabbitMQ 实战。

你握紧了粉拳:好的,我这就去学!
结尾
若干年后,你已经成为了大厂的消息队列专家。不仅能熟练使用 RabbitMQ 解决各种业务问题,搭个集群架构也是手拿把掐的。
你也像鱼皮当时一样,耐心地给新人分享学习 RabbitMQ 的经验,让他们谨记 "消息队列是实战型技术,一定要多动手实践"。

再次遇到鱼皮是在一条昏暗的小巷,此时的他年过 35,一毛不拔。你什么都没说,只是给他点了个赞。
不打扰,是你的温柔。
