RabbitMQ 学习笔记
hi,这些是我整理的rabbitmq相关的笔记,具体怎么安装在这里就不一一阐述了,不知道的可以问一下ai或者搜索相关视频,我将rabbitmq学习分为三天进行,最好对着步骤进行实操,效果更佳。 
一、RabbitMQ 是什么
RabbitMQ 是一个消息队列中间件,负责在两个系统之间传递消息。
打个比方:
- 你去餐厅吃饭,点完单后拿到一个号码牌(消息),后厨按号码牌顺序做菜(消费消息)
- 你不用站在厨房门口等,可以先去找座位(异步处理)
- 后厨忙不过来,单子在排队(流量削峰)
核心角色:
| 角色 | 作用 | 举例 |
|---|---|---|
| 生产者(Producer) | 发消息的人 | 用户下单,系统发一条消息 |
| 消费者(Consumer) | 收消息的人 | 库存服务收到消息,扣减库存 |
| 队列(Queue) | 存消息的地方 | 消息在队列里排队等待处理 |
| 交换机(Exchange) | 路由消息到队列 | 根据规则决定消息发给哪个队列 |
二、为什么需要 RabbitMQ
1. 异步处理
用户下单后,需要:扣库存、发短信、记日志。
不用 RabbitMQ:下单 → 扣库存 → 发短信 → 记日志 → 返回(全部串行,慢)
用 RabbitMQ:下单 → 扣库存 → 发消息到队列 → 返回(快,短信和日志异步处理)
2. 系统解耦
订单系统不直接调用库存系统、短信系统,而是发消息到队列,各系统自己来取消息。
好处:订单系统不需要知道谁在用这个消息,加新系统不用改订单代码。
3. 流量削峰
秒杀活动瞬间涌入大量请求,先全部放进队列排队,后端按自己能力慢慢处理,不会被压垮。
三、RabbitMQ 核心概念
1. 消息流转过程
生产者 → 交换机 → 队列 → 消费者
生产者不直接把消息发给队列,而是先发给交换机,交换机再根据规则转发到队列。
2. 交换机类型
| 类型 | 特点 | 场景 |
|---|---|---|
| Direct | 精确匹配路由键 | 点对点精确路由 |
| Fanout | 广播给所有绑定队列 | 消息广播 |
| Topic | 通配符匹配路由键 | 灵活的消息分类 |
| Headers | 按消息头匹配 | 很少使用 |
3. 路由键(Routing Key)
消息带的一个标签,交换机根据这个标签决定消息发给哪个队列。
例如:消息路由键是 order.pay,Direct 交换机只转发给绑定了 order.pay 的队列。
4. 绑定(Binding)
队列和交换机之间的连接关系,可以理解为"这个队列订阅了交换机的哪类消息"。
四、管理界面操作
启动 RabbitMQ 后,打开浏览器访问:http://localhost:15672
账号:guest,密码:guest
界面主要功能
| 页面 | 功能 |
|---|---|
| Overview | 查看整体状态、连接数、消息数量 |
| Connections | 查看当前连接 |
| Channels | 查看信道(一个连接可以有多个信道) |
| Exchanges | 查看和管理交换机 |
| Queues | 查看和管理队列 |
| Admin | 用户管理、权限管理 |
手动创建队列
- 点击顶部 Queues 标签
- 点击 Add a new queue
- Name 填
test_queue - 点击 Add queue
手动发送消息
- 进入刚创建的队列
test_queue - 展开 Publish message
- Payload 填
hello rabbitmq - 点击 Publish message
手动接收消息
- 在队列页面展开 Get messages
- 点击 Get Message(s)
- 可以看到刚才发送的消息内容
五、五种消息模式
1. 简单模式(Simple)
最基础的模式,一个生产者,一个队列,一个消费者。
生产者 → 队列 → 消费者
- 生产者往队列里发消息
- 消费者从队列里取消息
- 一对一,一条消息只被一个消费者处理
2. 工作队列模式(Work Queue)
一个生产者,一个队列,多个消费者。
markdown
生产者 → 队列 → 消费者1
→ 消费者2
→ 消费者3
多个消费者竞争消费同一个队列的消息,默认轮询分发:每人一条,平均分配。
适合场景:任务分发,多个工作者抢活干。
3. 发布订阅模式(Fanout)
一个生产者,一个 Fanout 交换机,多个队列,多个消费者。
markdown
生产者 → Fanout交换机 → 队列1 → 消费者1
→ 队列2 → 消费者2
Fanout 交换机把消息广播给所有绑定的队列,每个队列都能收到完整的一份消息。
适合场景:一条消息需要多个系统同时处理(如同时通知库存系统和日志系统)。
4. 路由模式(Direct)
一个生产者,一个 Direct 交换机,多个队列。
go
生产者 → Direct交换机 →(路由键=error)→ 队列1(绑定了error)→ 消费者1
→(路由键=info) → 队列2(绑定了info) → 消费者2
Direct 交换机根据消息的路由键精确匹配,只转发给路由键匹配的队列。
适合场景:按消息类型分流,错误日志和普通日志分开处理。
5. 主题模式(Topic)
一个生产者,一个 Topic 交换机,多个队列。
css
生产者 → Topic交换机 →(路由键=order.pay)→ 队列1(绑定了order.*)→ 消费者1
→ 队列2(绑定了order.#)→ 消费者2
Topic 交换机支持通配符匹配:
| 通配符 | 含义 | 示例 |
|---|---|---|
* |
匹配恰好一个词 | order.* 匹配 order.pay,不匹配 order.pay.success |
# |
匹配零个或多个词 | order.# 匹配 order.pay、order.pay.success |
适合场景:灵活的消息分类,比如按模块和操作进行多级分类。
六、五种模式对比
| 模式 | 交换机 | 消费者数 | 消息去向 |
|---|---|---|---|
| 简单模式 | 无(直连队列) | 1个 | 只有一个消费者收到 |
| 工作队列 | 无(直连队列) | 多个 | 轮询分配,每人一条 |
| 发布订阅 | Fanout | 多个 | 每个队列都收到一份 |
| 路由模式 | Direct | 多个 | 只有路由键匹配的队列收到 |
| 主题模式 | Topic | 多个 | 通配符匹配的队列收到 |
七、Day 1 小结
- RabbitMQ 是消息队列,负责在系统之间异步传递消息
- 三个核心作用:异步处理、系统解耦、流量削峰
- 消息流转:生产者 → 交换机 → 队列 → 消费者
- 四种交换机:Direct(精确)、Fanout(广播)、Topic(通配符)、Headers(很少用)
- 五种模式:简单、工作队列、发布订阅、路由、主题
八、Day 2:Spring Boot 集成 RabbitMQ
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: guest
virtual-host: /
virtual-host 是虚拟主机,类似 Redis 的 database,用来隔离不同业务的消息,默认用 /。
3. 发送消息(RabbitTemplate)
java
@Autowired
private RabbitTemplate rabbitTemplate;
// 发送消息到指定队列(简单模式)
rabbitTemplate.convertAndSend("队列名", "消息内容");
// 发送到交换机(带路由键)
rabbitTemplate.convertAndSend("交换机名", "路由键", "消息内容");
convertAndSend 会自动把 Java 对象转成字节数组发送。
4. 接收消息(@RabbitListener)
java
@RabbitListener(queues = "队列名")
public void receiveMessage(String message) {
System.out.println("收到消息:" + message);
}
加上 @RabbitListener 注解后,只要队列里有消息,Spring 会自动调用这个方法。
5. 配置队列和交换机(Java 配置类)
java
@Configuration
public class RabbitMQConfig {
// 声明队列
@Bean
public Queue testQueue() {
return new Queue("test_queue", true); // true=持久化
}
// 声明 Direct 交换机
@Bean
public DirectExchange directExchange() {
return new DirectExchange("direct_exchange");
}
// 绑定:队列绑定到交换机,指定路由键
@Bean
public Binding binding(Queue testQueue, DirectExchange directExchange) {
return BindingBuilder.bind(testQueue)
.to(directExchange)
.with("test_routing_key");
}
}
这段代码做了三件事:
- 创建一个叫
test_queue的队列 - 创建一个叫
direct_exchange的 Direct 交换机 - 把队列绑定到交换机,路由键是
test_routing_key
6. 四种交换机类型代码对比
Direct 交换机(精确匹配):
java
@Bean
public DirectExchange directExchange() {
return new DirectExchange("direct_exchange");
}
// 发送时指定路由键
rabbitTemplate.convertAndSend("direct_exchange", "error", "错误消息");
// 只有绑定了 "error" 的队列能收到
Fanout 交换机(广播):
java
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("fanout_exchange");
}
// 发送时不需要路由键
rabbitTemplate.convertAndSend("fanout_exchange", "", "广播消息");
// 所有绑定的队列都能收到
Topic 交换机(通配符):
java
@Bean
public TopicExchange topicExchange() {
return new TopicExchange("topic_exchange");
}
// 发送
rabbitTemplate.convertAndSend("topic_exchange", "order.pay", "支付消息");
// 绑定了 "order.*" 和 "order.#" 的队列都能收到
7. 消息确认机制
生产者确认 --- 消息有没有成功到达 RabbitMQ:
yaml
spring:
rabbitmq:
publisher-confirm-type: correlated # 开启确认
消费者确认 --- 消费者有没有成功处理消息:
yaml
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual # 手动确认(更安全)
手动确认代码:
java
@RabbitListener(queues = "test_queue")
public void receiveMessage(Message message, Channel channel) throws IOException {
try {
// 处理消息
System.out.println("收到:" + new String(message.getBody()));
// 确认消息(第二个参数false=只确认当前这条)
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
// 处理失败,拒绝消息(第三个参数true=重回队列)
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
}
}
两种确认方式对比:
| 方式 | 配置 | 特点 |
|---|---|---|
| 自动确认 | acknowledge-mode: auto(默认) |
收到就确认,快但可能丢消息 |
| 手动确认 | acknowledge-mode: manual |
处理完再确认,安全但代码复杂 |
九、Day 2 小结
- Spring Boot 集成 :引入
spring-boot-starter-amqp,配置 host/port/username/password - 发送消息 :
rabbitTemplate.convertAndSend(交换机, 路由键, 消息) - 接收消息 :
@RabbitListener(queues = "队列名")自动监听 - 配置类:声明队列、交换机、绑定关系
- 四种交换机:Direct 精确、Fanout 广播、Topic 通配符、Headers 很少用
- 消息确认:生产者确认 + 消费者手动确认,防止消息丢失
十、Day 3:死信队列 + 延迟队列 + 消息幂等性
1. 死信队列(DLX)
什么是死信? 消息"死了",原因有三种:
- 消息被消费者拒绝(basicNack 且 requeue=false)
- 消息过期了(设置了 TTL 但没人消费)
- 队列满了,新消息进不来
死信队列的作用: 给"死掉"的消息一个归宿,而不是直接丢掉。
配置方式:
java
@Bean
public Queue normalQueue() {
Map<String, Object> args = new HashMap<>();
// 指定死信交换机
args.put("x-dead-letter-exchange", "dead_exchange");
// 指定死信路由键
args.put("x-dead-letter-routing-key", "dead_routing_key");
return new Queue("normal_queue", true, false, false, args);
}
@Bean
public Queue deadQueue() {
return new Queue("dead_queue", true);
}
消息在 normal_queue 里"死了",会自动转到 dead_queue。
2. 延迟队列
场景: 用户下单后30分钟未支付,自动取消订单。
RabbitMQ 原生没有延迟队列,用 TTL + 死信队列 实现:
markdown
生产者 → 发送带TTL的消息 → 延迟队列(没有消费者)
↓ 30分钟后消息过期(变成死信)
死信交换机 → 死信队列 → 消费者(检查订单状态,取消未支付订单)
java
// 发送延迟消息(30分钟过期)
rabbitTemplate.convertAndSend("delay_exchange", "order_delay", orderMessage,
message -> {
message.getMessageProperties().setExpiration("1800000"); // 30分钟,单位毫秒
return message;
});
3. 消息幂等性
问题: 消费者可能收到重复消息(网络重试等原因),导致业务执行两次。
解决方案: 消息带唯一 ID,消费前先用 Redis 判断是否已处理。
java
@RabbitListener(queues = "dead_queue")
public void handleMessage(Message message, Channel channel) throws IOException {
String msgId = message.getMessageProperties().getMessageId();
// 用 Redis 判断是否已处理过(SETNX:只有第一次能设置成功)
Boolean result = redisTemplate.opsForValue()
.setIfAbsent("msg:" + msgId, "1", 24, TimeUnit.HOURS);
if (result == null || !result) {
// 已经处理过,直接确认,跳过
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
return;
}
try {
// 首次处理,执行业务逻辑
processOrder(message);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
}
}
setIfAbsent 相当于 Redis 的 SETNX,只有第一次能设置成功返回 true,之后都返回 false,从而保证消息只处理一次。
4. 常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 消息堆积 | 消费者处理太慢 | 增加消费者数量、提高消费速度 |
| 消息丢失 | 没有确认机制或没持久化 | 开启确认机制 + 队列和消息都持久化 |
| 消费者挂了 | 自动确认模式下消息被丢失 | 改用手动确认,未确认的消息重回队列 |
| 重复消费 | 网络重试导致消息被发两次 | 消息幂等性(Redis SETNX 去重) |
十一、三天学习总结
核心知识点
| 知识点 | 内容 |
|---|---|
| 消息队列作用 | 异步处理、系统解耦、流量削峰 |
| 消息流转 | 生产者 → 交换机 → 队列 → 消费者 |
| 交换机类型 | Direct 精确、Fanout 广播、Topic 通配符 |
| Spring Boot 集成 | starter-amqp + RabbitTemplate + @RabbitListener |
| 消息确认 | 生产者确认 + 消费者手动确认 |
| 死信队列 | 处理失败/过期/队列满的消息 |
| 延迟队列 | TTL + 死信队列实现订单超时取消 |
| 消息幂等性 | Redis SETNX 防止重复消费 |