这是我学习 RabbitMQ 时整理的入门笔记。消息队列是分布式系统里绕不开的组件,秒杀系统的异步削峰、订单超时、日志收集、应用解耦都用它。这篇先把 RabbitMQ 的核心概念和上手流程过一遍,代码按 Spring Boot 落地。
一、为什么需要消息队列
先把"解决什么问题"想清楚,否则学了概念也不知道用在哪。消息队列解决三类问题:
- 异步削峰:秒杀下单一瞬间流量巨大,直接同步写库会打爆数据库。先丢进队列,消费者按自己的速度慢慢处理——把"瞬间高峰"变成"平稳流量"。
- 应用解耦:下单后要发短信、发邮件、更新库存。如果同步调用,任何一个下游挂掉或变慢都会拖垮主流程;通过队列,主流程只依赖队列,下游自己订阅。
- 流量削峰/缓冲:日志采集、大数据上报这种"量大但不紧急"的场景,队列做缓冲,消费者批量处理。
一句话:生产者不关心谁在消费,消费者不关心谁在发消息,中间只有队列。
二、核心概念(先记这六个)
RabbitMQ 的模型可以拆成六个词,记住它们就懂了大半:
| 概念 | 说明 |
|---|---|
| Producer(生产者) | 发消息的一方 |
| Consumer(消费者) | 收消息并处理的一方 |
| Queue(队列) | 消息的暂存地,本质是一个有界/无界缓冲 |
| Exchange(交换机) | 消息的"路由器",决定消息发给哪些队列 |
| Binding(绑定) | 交换机和队列之间的连接关系(带路由规则) |
| Routing Key(路由键) | 消息携带的"地址",交换机按它路由 |
消息流转:Producer → Exchange →(按 Binding 规则)→ Queue → Consumer
注意:生产者从来不直接往队列发消息,而是发给交换机,交换机按绑定规则把消息路由到队列——这是 RabbitMQ 和 Redis 的 List 这类"直接队列"最大的区别。
三、交换机类型(重点)
交换机是 RabbitMQ 的核心,一共四种类型,决定了消息怎么路由:
| 类型 | 路由规则 | 典型场景 |
|---|---|---|
| Direct | Routing Key 精确匹配 | 按业务类型精确分发 |
| Fanout | 忽略 Routing Key,广播给所有绑定队列 | 广播通知 |
| Topic | Routing Key 通配符匹配(* 一个词,# 多个词) | 按主题订阅(如 order.*) |
| Headers | 按消息头(Header)匹配 | 很少用,一般被 Topic 替代 |
理解 Fanout 和 Topic 的差别就够了:
- Fanout:发一条,所有绑定的队列都收到(广播,无差别)
- Topic:发一条带路由键的消息(如
order.created),只有绑定了匹配模式的队列收到(order.*收,payment.*不收)
四、Docker 安装 + 管理界面
本地开发用 Docker 一条命令起:
bash
docker run -d --name rabbitmq \
-p 5672:5672 -p 15672:15672 \
rabbitmq:3.13-management5672:AMQP 协议端口(程序连接用)15672:管理界面端口(浏览器访问http://localhost:15672,默认账号guest/guest)
管理界面里能看到队列、交换机、连接、消息积压量,排查问题第一站。
五、Spring Boot 集成实战
1. 依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>2. 配置
yaml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest3. 声明交换机、队列、绑定(Java Config)
java
@Configuration
public class RabbitConfig {
public static final String EXCHANGE = "demo.exchange";
public static final String QUEUE = "demo.queue";
public static final String ROUTING_KEY = "demo.rk";
@Bean
public DirectExchange demoExchange() {
// durable=true:交换机持久化,重启不丢
return new DirectExchange(EXCHANGE, true, false);
}
@Bean
public Queue demoQueue() {
// durable=true:队列持久化(存到磁盘)
return new Queue(QUEUE, true);
}
@Bean
public Binding demoBinding() {
return BindingBuilder.bind(demoQueue()).to(demoExchange()).with(ROUTING_KEY);
}
}4. 发送消息
java
@Service
public class MsgSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(String msg) {
rabbitTemplate.convertAndSend(
RabbitConfig.EXCHANGE,
RabbitConfig.ROUTING_KEY,
msg
);
}
}5. 接收消息(消费者)
java
@Component
public class MsgListener {
@RabbitListener(queues = RabbitConfig.QUEUE)
public void onMessage(String message) {
System.out.println("收到消息: " + message);
}
}启动应用,调一次 send(),控制台就能看到消费者打印——最简单的生产者/消费者链路就跑通了。
六、入门就要知道的三个"坑"
这几个问题初学者几乎必踩,先打个预防针(详细方案见我整理的系统架构 FAQ 里"消息队列怎么防丢消息"一节):
- 消息丢了:生产者没开 confirm、队列/消息没设置持久化、消费者自动 ack 后处理失败——三层里任何一层漏了都会丢。
- 重复消费:MQ 只保证"至少一次",消费者必须自己做幂等(唯一键/状态机),别指望 MQ 不重投。
- 乱序:RabbitMQ 单队列单消费者天然有序;多消费者抢消息就可能乱序,需要业务侧排序或保证单消费者。
小结
- RabbitMQ = 交换机路由的消息队列,六个核心概念(生产者/消费者/队列/交换机/绑定/路由键)
- 四种交换机:Direct 精确匹配、Fanout 广播、Topic 通配符、Headers 按头(少用)
- Spring Boot 集成三步:依赖 + 配置 + 声明交换机/队列/绑定,
RabbitTemplate发、@RabbitListener收 - 生产可用三件事:持久化 + 手动 ack + 消费者幂等
下一步可以看我的《秒杀系统架构分析》里 RabbitMQ 怎么在真实场景做异步削峰,或者深入研究死信队列、延迟队列(插件)、消息确认机制。
