一、RabbitMQ 是什么?
1.1 定义
RabbitMQ 是一个开源的消息中间件(Message Broker),基于 AMQP(Advanced Message Queuing Protocol)协议实现,使用 Erlang 语言开发。
核心定位:
┌─────────────────────────────────────────────┐
│ 消息中间件(Message Broker) │
│ │
│ ├─ 异步通信 │
│ ├─ 应用解耦 │
│ ├─ 流量削峰 │
│ ├─ 消息路由 │
│ └─ 可靠传递 │
└─────────────────────────────────────────────┘
1.2 消息队列的演进
同步调用(传统方式):
应用 A ──────直接调用──────> 应用 B
│ │
└────── 等待响应 ─────────┘
问题:
├─ 强耦合
├─ 同步阻塞
└─ 性能瓶颈
异步调用(消息队列):
应用 A ───发送消息──> [消息队列] ───消费消息──> 应用 B
│ │
└── 立即返回 └── 异步处理
优势:
├─ 解耦
├─ 异步
├─ 削峰
└─ 可靠
1.3 RabbitMQ vs 其他消息队列
| 特性 | RabbitMQ | Kafka | RocketMQ | ActiveMQ |
|---|---|---|---|---|
| 协议 | AMQP | 自定义 | 自定义 | JMS、AMQP |
| 语言 | Erlang | Scala/Java | Java | Java |
| 吞吐量 | 万级 | 百万级 | 十万级 | 万级 |
| 时效性 | 微秒级 | 毫秒级 | 毫秒级 | 毫秒级 |
| 可用性 | 高(主从) | 非常高(分布式) | 高(主从) | 高 |
| 消息丢失 | 低 | 理论不丢 | 低 | 低 |
| 功能特性 | 丰富(路由) | 简单(日志) | 丰富 | 较丰富 |
| 适用场景 | 复杂路由、实时性 | 大数据、日志 | 金融、电商 | 企业集成 |
| 社区支持 | 活跃 | 非常活跃 | 活跃 | 较少 |
1.4 RabbitMQ 特点
核心特点:
1. 可靠性
├─ 消息持久化
├─ 消息确认(ACK)
├─ 发布确认(Confirm)
└─ 高可用(镜像队列)
2. 灵活的路由
├─ Direct(直连)
├─ Topic(主题)
├─ Fanout(广播)
└─ Headers(头部)
3. 集群与高可用
├─ 集群模式
├─ 镜像队列
└─ 联邦模式
4. 多语言支持
├─ Java、Python、Go
├─ .NET、PHP、Ruby
└─ 几乎所有主流语言
5. 管理界面
├─ Web 管理控制台
├─ HTTP API
└─ 命令行工具
6. 插件生态
├─ 延迟队列
├─ 消息追踪
└─ 联邦/Shovel
二、RabbitMQ 核心作用
2.1 应用解耦
问题场景:订单系统需要调用库存、支付、物流等多个系统
传统方式(强耦合):
java
public class OrderService {
@Autowired
private InventoryService inventoryService; // 库存服务
@Autowired
private PaymentService paymentService; // 支付服务
@Autowired
private LogisticsService logisticsService; // 物流服务
public void createOrder(Order order) {
// 1. 创建订单
saveOrder(order);
// 2. 调用库存服务(强依赖)
inventoryService.decreaseStock(order);
// 3. 调用支付服务(强依赖)
paymentService.pay(order);
// 4. 调用物流服务(强依赖)
logisticsService.ship(order);
// 问题:
// - 任何一个服务挂了,整个流程失败
// - 新增服务需要修改代码
// - 各服务强耦合
}
}
消息队列方式(解耦):
java
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void createOrder(Order order) {
// 1. 创建订单
saveOrder(order);
// 2. 发送消息到消息队列
rabbitTemplate.convertAndSend("order.exchange", "order.created", order);
// 完成!订单服务不关心后续处理
}
}
// 库存服务(独立消费)
@RabbitListener(queues = "inventory.queue")
public void handleOrder(Order order) {
decreaseStock(order);
}
// 支付服务(独立消费)
@RabbitListener(queues = "payment.queue")
public void handleOrder(Order order) {
pay(order);
}
// 优势:
// - 订单服务不依赖其他服务
// - 新增服务只需订阅消息
// - 各服务独立开发、部署
2.2 异步处理
问题场景:用户注册需要发送邮件、短信
同步处理(慢):
java
public void register(User user) {
// 1. 保存用户(50ms)
saveUser(user);
// 2. 发送邮件(200ms)
sendEmail(user);
// 3. 发送短信(100ms)
sendSMS(user);
// 总耗时:350ms
// 用户等待 350ms 才能看到注册成功
}
异步处理(快):
java
public void register(User user) {
// 1. 保存用户(50ms)
saveUser(user);
// 2. 发送消息(1ms)
rabbitTemplate.convertAndSend("user.exchange", "user.registered", user);
// 总耗时:51ms
// 用户立即看到注册成功
// 邮件、短信异步发送
}
// 邮件服务异步消费
@RabbitListener(queues = "email.queue")
public void sendEmail(User user) {
// 异步发送邮件
}
// 短信服务异步消费
@RabbitListener(queues = "sms.queue")
public void sendSMS(User user) {
// 异步发送短信
}
性能对比:
同步处理:350ms(用户等待)
异步处理:51ms(用户无感知)
性能提升:7倍
2.3 流量削峰
问题场景:秒杀活动,瞬间 10 万请求
无消息队列(系统崩溃):
秒杀开始
│
10万请求 ──────> 应用服务器(最多处理1000 QPS)
│
├─ 9.9万请求被拒绝
├─ 数据库崩溃
└─ 系统宕机
有消息队列(削峰):
秒杀开始
│
10万请求 ──────> 应用服务器
│
├─ 快速写入消息队列
└─ 立即返回"排队中"
│
↓
[消息队列](缓冲)
100,000 条消息
│
↓
消费者(按自己的速度处理)
1000 QPS 稳定消费
│
↓
100秒处理完成
系统稳定运行
代码示例:
java
// 秒杀控制器
@PostMapping("/seckill")
public String seckill(Long productId, Long userId) {
// 1. 快速写入消息队列
SeckillMessage msg = new SeckillMessage(productId, userId);
rabbitTemplate.convertAndSend("seckill.exchange", "", msg);
// 2. 立即返回
return "排队中,请稍候查询结果";
}
// 秒杀消费者(控制处理速度)
@RabbitListener(queues = "seckill.queue")
public void handleSeckill(SeckillMessage msg) {
// 按照系统能力处理(1000 QPS)
seckillService.doSeckill(msg.getProductId(), msg.getUserId());
}
2.4 数据分发
问题场景:订单创建后,需要通知多个系统
订单系统创建订单
│
├──> 库存系统(扣减库存)
├──> 支付系统(发起支付)
├──> 物流系统(准备发货)
├──> 积分系统(增加积分)
└──> 数据分析系统(统计)
使用 Fanout Exchange(广播模式)
java
// 订单服务发送消息
rabbitTemplate.convertAndSend("order.fanout", "", order);
// 多个系统独立消费
@RabbitListener(queues = "inventory.queue") // 库存系统
@RabbitListener(queues = "payment.queue") // 支付系统
@RabbitListener(queues = "logistics.queue") // 物流系统
@RabbitListener(queues = "points.queue") // 积分系统
@RabbitListener(queues = "analytics.queue") // 数据分析
三、核心概念与架构
3.1 核心组件
RabbitMQ 核心组件:
┌─────────────┐
│ Producer │ 生产者(发送消息)
│ (生产者) │
└──────┬──────┘
│ 发送消息
↓
┌─────────────────────────────────────────────────┐
│ RabbitMQ Broker │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Exchange │──────│ Queue │ │
│ │ (交换机) │ 路由 │ (队列) │ │
│ └──────────────┘ └──────┬───────┘ │
│ │ │ │
│ 绑定关系 消息存储 │
│ (Binding) │
│ │ │ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Binding │ │ Message │ │
│ │ (绑定) │ │ (消息) │ │
│ └──────────────┘ └──────────────┘ │
│ │
└────────────────────────────┬────────────────────┘
│ 消费消息
↓
┌─────────────┐
│ Consumer │ 消费者(接收消息)
│ (消费者) │
└─────────────┘
3.2 核心概念详解
3.2.1 Producer(生产者)
生产者:
├─ 创建消息
├─ 发送到 Exchange
└─ 不关心消息如何路由
示例:
Producer → Message(routingKey="order.created") → Exchange
3.2.2 Exchange(交换机)
交换机的作用:
├─ 接收生产者发送的消息
├─ 根据路由规则分发到队列
└─ 4种类型:Direct、Topic、Fanout、Headers
交换机属性:
├─ Name:交换机名称
├─ Type:类型(direct/topic/fanout/headers)
├─ Durability:是否持久化
├─ Auto-delete:无队列绑定时自动删除
└─ Arguments:扩展参数
4种交换机类型:
| 类型 | 路由规则 | 使用场景 |
|---|---|---|
| Direct | 精确匹配 routing key | 简单路由、点对点 |
| Topic | 模式匹配(通配符) | 复杂路由、订阅 |
| Fanout | 广播(忽略 routing key) | 消息广播 |
| Headers | 匹配消息头属性 | 复杂匹配 |
3.2.3 Queue(队列)
队列:
├─ 存储消息(缓冲区)
├─ FIFO(先进先出)
├─ 消费者从队列取消息
└─ 可以设置持久化、优先级、TTL等
队列属性:
├─ Name:队列名称
├─ Durable:是否持久化
├─ Exclusive:是否独占
├─ Auto-delete:无消费者时自动删除
├─ Arguments:
│ ├─ x-message-ttl:消息过期时间
│ ├─ x-max-length:队列最大长度
│ ├─ x-max-priority:最大优先级
│ └─ x-dead-letter-exchange:死信交换机
└─ ...
3.2.4 Binding(绑定)
绑定:
├─ 连接 Exchange 和 Queue
├─ 包含 routing key(路由键)
└─ 定义消息如何路由
示例:
Exchange(order.exchange) ──[routing key: order.created]──> Queue(order.queue)
3.2.5 Message(消息)
消息组成:
├─ 消息体(Body)
│ └─ 实际的业务数据(字节数组)
│
├─ 属性(Properties)
│ ├─ content-type:内容类型(application/json)
│ ├─ content-encoding:编码(UTF-8)
│ ├─ delivery-mode:投递模式(1=非持久化,2=持久化)
│ ├─ priority:优先级(0-9)
│ ├─ correlation-id:关联ID
│ ├─ reply-to:回复队列
│ ├─ expiration:过期时间
│ ├─ message-id:消息ID
│ ├─ timestamp:时间戳
│ ├─ type:消息类型
│ ├─ user-id:用户ID
│ └─ app-id:应用ID
│
└─ 自定义头(Headers)
└─ key-value 键值对
3.2.6 Consumer(消费者)
消费者:
├─ 订阅队列
├─ 接收消息
├─ 处理业务逻辑
└─ 发送 ACK(确认)
消费模式:
├─ Push(推模式):服务器主动推送(推荐)
└─ Pull(拉模式):客户端主动拉取
确认模式:
├─ 自动确认(autoAck=true):收到消息立即确认
├─ 手动确认(autoAck=false):处理完成后确认
└─ 不确认:消息重新入队
3.3 消息流转过程
完整的消息流转:
1. 生产者发送消息
Producer
│
├─ 创建消息(Message)
├─ 指定交换机(Exchange)
├─ 指定路由键(Routing Key)
└─ 发送(publish)
↓
2. 到达 RabbitMQ Broker
Connection(连接)
├─ TCP 连接
└─ Channel(信道)
↓
3. 路由到交换机
Exchange
├─ 接收消息
├─ 根据类型和路由键匹配
└─ 分发到绑定的队列
↓
4. 存储到队列
Queue
├─ 存储消息(内存/磁盘)
├─ 等待消费者
└─ 维护消费位置
↓
5. 消费者消费
Consumer
├─ 订阅队列(subscribe)
├─ 接收消息(deliver)
├─ 处理业务逻辑
└─ 发送确认(ACK)
↓
6. 删除消息
RabbitMQ
└─ 收到 ACK 后删除消息
3.4 连接与信道
连接与信道的关系:
Application
│
├─ Connection(TCP 连接)
│ ├─ 耗资源、重量级
│ └─ 一个应用通常只有1个连接
│
└─ Channel(信道,轻量级)
├─ 在 Connection 内的虚拟连接
├─ 多路复用
├─ 独立的 AMQP 会话
└─ 一个 Connection 可以有多个 Channel
┌─────────────────────────────────────────┐
│ Application │
│ │
│ ┌────────────────────────────────────┐ │
│ │ Connection (TCP) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │Channel 1 │ │Channel 2 │ ... │ │
│ │ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────┘ │
└─────────────────────────────────────────┘
│
↓
RabbitMQ Broker
为什么需要 Channel?
├─ 减少 TCP 连接数(Connection 重量级)
├─ 提高并发(一个连接支持多个信道)
└─ 线程安全(每个线程一个 Channel)
四、底层原理详解
4.1 AMQP 协议
4.1.1 AMQP 概述
AMQP(Advanced Message Queuing Protocol)
高级消息队列协议
协议特点:
├─ 应用层协议(类似 HTTP)
├─ 二进制协议(高效)
├─ 跨平台、跨语言
└─ 支持多种消息模式
协议版本:
├─ AMQP 0-9-1(RabbitMQ 主要支持)
└─ AMQP 1.0(RabbitMQ 通过插件支持)
4.1.2 AMQP 模型
AMQP 模型:
Publisher ──publish──> Exchange ──routing──> Queue ──consume──> Consumer
│ │
│ │
绑定规则 消息存储
(Binding) (Message)
4.1.3 AMQP 帧结构
AMQP 帧(Frame):
┌──────────────────────────────────────┐
│ Frame Header (7 bytes) │
│ ├─ Type (1 byte):帧类型 │
│ ├─ Channel (2 bytes):信道ID │
│ └─ Size (4 bytes):帧大小 │
├──────────────────────────────────────┤
│ Payload(可变长度) │
│ └─ 实际数据内容 │
├──────────────────────────────────────┤
│ Frame End (1 byte):帧结束标记(0xCE) │
└──────────────────────────────────────┘
帧类型:
├─ 1:Method Frame(方法帧)
├─ 2:Header Frame(内容头帧)
├─ 3:Body Frame(消息体帧)
└─ 8:Heartbeat Frame(心跳帧)
4.2 消息投递流程
4.2.1 发送消息流程
1. 创建连接
Client ──TCP Handshake──> RabbitMQ
├─ AMQP 协议握手
├─ 认证(用户名/密码)
└─ 虚拟主机选择
2. 创建信道
Connection ──Open Channel──> Channel
└─ 获取 Channel ID
3. 声明交换机(可选)
Channel ──Exchange.Declare──> Exchange
├─ 交换机名称
├─ 类型(direct/topic/fanout/headers)
├─ 持久化(durable)
└─ 其他参数
4. 声明队列(可选)
Channel ──Queue.Declare──> Queue
├─ 队列名称
├─ 持久化(durable)
├─ 独占(exclusive)
└─ 自动删除(auto-delete)
5. 绑定队列(可选)
Channel ──Queue.Bind──> Binding
├─ 队列名称
├─ 交换机名称
└─ 路由键(routing key)
6. 发送消息
Channel ──Basic.Publish──> Message
├─ 交换机名称
├─ 路由键
├─ 消息属性
└─ 消息体
↓
7. 路由消息
Exchange ──Route──> Queue
├─ 根据交换机类型
├─ 根据路由键
└─ 匹配绑定关系
↓
8. 存储消息
Queue ──Store──> Disk/Memory
└─ 根据持久化设置
4.2.2 消费消息流程
1. 订阅队列
Consumer ──Basic.Consume──> Queue
├─ 队列名称
├─ 消费者标签(consumer tag)
├─ 自动确认(auto ack)
└─ 其他参数
2. 接收消息(Push 模式)
Queue ──Basic.Deliver──> Consumer
├─ 消息属性
├─ 消息体
└─ delivery tag(投递标签)
3. 处理消息
Consumer
└─ 执行业务逻辑
4. 确认消息
Consumer ──Basic.Ack──> RabbitMQ
└─ delivery tag
↓
5. 删除消息
RabbitMQ
└─ 从队列删除消息
拒绝消息(可选):
Consumer ──Basic.Nack/Reject──> RabbitMQ
├─ delivery tag
└─ requeue(是否重新入队)
4.3 路由算法
4.3.1 Direct Exchange 路由
java
// 精确匹配路由键
public void route(Message message, String routingKey) {
for (Binding binding : bindings) {
if (binding.getRoutingKey().equals(routingKey)) {
queue.enqueue(message);
}
}
}
示例:
Exchange: order.direct
Bindings:
├─ routing key: order.created → queue: order.queue
├─ routing key: order.paid → queue: payment.queue
└─ routing key: order.shipped → queue: logistics.queue
消息: routing key = "order.created"
结果: 只路由到 order.queue
4.3.2 Topic Exchange 路由
java
// 模式匹配(支持通配符)
// * 匹配一个单词
// # 匹配零个或多个单词
public void route(Message message, String routingKey) {
for (Binding binding : bindings) {
if (matchPattern(binding.getPattern(), routingKey)) {
queue.enqueue(message);
}
}
}
示例:
Exchange: order.topic
Bindings:
├─ pattern: order.* → queue: all-orders.queue
├─ pattern: order.created → queue: order.queue
├─ pattern: order.# → queue: archive.queue
└─ pattern: *.paid → queue: payment.queue
消息: routing key = "order.created"
匹配:
├─ order.* ✅ 匹配(order.created)
├─ order.created ✅ 匹配
├─ order.# ✅ 匹配(order.created)
└─ *.paid ❌ 不匹配
结果: 路由到 all-orders.queue、order.queue、archive.queue
4.3.3 Fanout Exchange 路由
java
// 广播模式(忽略路由键)
public void route(Message message, String routingKey) {
for (Binding binding : bindings) {
queue.enqueue(message); // 发送到所有绑定的队列
}
}
示例:
Exchange: order.fanout
Bindings:
├─ queue: inventory.queue
├─ queue: payment.queue
└─ queue: logistics.queue
消息: routing key = "任意值"
结果: 路由到所有绑定的队列
4.4 消息确认机制
4.4.1 生产者确认(Publisher Confirm)
发送流程(Confirm 模式):
Producer ──[1] Publish Message──> RabbitMQ
│
[2] 路由消息
├─ 成功路由到队列
│ └─> [3] Basic.Ack
│ │
Producer <──────[4] 收到确认──────────┘
│
└─ 路由失败
└─> [3] Basic.Nack
│
Producer <──────[4] 收到拒绝──────────────────────────┘
代码示例:
channel.confirmSelect(); // 启用确认模式
channel.basicPublish(exchange, routingKey, props, body);
channel.waitForConfirms(); // 等待确认
4.4.2 消费者确认(Consumer ACK)
消费流程(手动确认):
RabbitMQ ──[1] Deliver Message──> Consumer
│
[2] 处理消息
│
┌───┴───┐
│ │
[3a] 成功 [3b] 失败
│ │
Basic.Ack Basic.Nack
│ │
RabbitMQ <────[4a] 删除消息────────┘ │
│
RabbitMQ <────[4b] 重新入队────────────────┘
确认方式:
├─ Basic.Ack:确认消息(删除)
├─ Basic.Nack:拒绝消息(可重新入队)
└─ Basic.Reject:拒绝单条消息
代码示例:
channel.basicConsume(queue, false, (tag, message) -> {
try {
processMessage(message);
channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
} catch (Exception e) {
channel.basicNack(message.getEnvelope().getDeliveryTag(), false, true);
}
});
4.5 消息持久化
持久化三要素(全部开启才能保证消息不丢失):
1. 交换机持久化
channel.exchangeDeclare(
"order.exchange",
"direct",
true // durable = true(持久化)
);
2. 队列持久化
channel.queueDeclare(
"order.queue",
true, // durable = true(持久化)
false,
false,
null
);
3. 消息持久化
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.deliveryMode(2) // 2 = 持久化,1 = 非持久化
.build();
channel.basicPublish(exchange, routingKey, props, body);
持久化流程:
Message ──> RabbitMQ
│
[1] 接收消息
│
[2] 写入磁盘
├─ 消息内容
├─ 索引信息
└─ 元数据
│
[3] 返回确认
│
[4] 内存缓存
│
[5] 异步刷盘
五、数据存储机制
5.1 存储架构
RabbitMQ 存储架构:
┌────────────────────────────────────────────┐
│ 内存存储(RAM) │
│ ├─ 队列元数据 │
│ ├─ 交换机元数据 │
│ ├─ 绑定关系 │
│ └─ 消息索引 │
└────────────────────────────────────────────┘
│
↓
┌────────────────────────────────────────────┐
│ 磁盘存储(Mnesia + 文件) │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ Mnesia 数据库(元数据) │ │
│ │ ├─ 队列定义 │ │
│ │ ├─ 交换机定义 │ │
│ │ ├─ 绑定关系 │ │
│ │ ├─ 虚拟主机 │ │
│ │ └─ 用户权限 │ │
│ └──────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ 消息存储(Message Store) │ │
│ │ ├─ 消息内容 │ │
│ │ ├─ 消息索引 │ │
│ │ └─ 持久化队列消息 │ │
│ └──────────────────────────────────────┘ │
└────────────────────────────────────────────┘
5.2 Mnesia 数据库
Mnesia(Erlang 内置分布式数据库):
作用:
├─ 存储 RabbitMQ 元数据
├─ 支持集群
└─ 支持事务
存储内容:
├─ Virtual Host(虚拟主机)
├─ Exchange(交换机)
├─ Queue(队列)
├─ Binding(绑定)
├─ User(用户)
├─ Permission(权限)
└─ Policy(策略)
存储位置:
└─ /var/lib/rabbitmq/mnesia/
文件结构:
/var/lib/rabbitmq/mnesia/rabbit@hostname/
├─ DECISION_TAB.LOG
├─ LATEST.LOG
├─ nodes_running_at_shutdown
├─ rabbit_durable_exchange.DCD
├─ rabbit_durable_queue.DCD
├─ rabbit_durable_route.DCD
└─ schema.DAT
5.3 消息存储
5.3.1 消息存储文件
消息存储目录:
/var/lib/rabbitmq/mnesia/rabbit@hostname/msg_stores/vhosts/
文件类型:
├─ .rdq(Queue Index):队列索引文件
│ ├─ 消息位置
│ ├─ 消息状态(未投递/已投递/已确认)
│ └─ 消息序号
│
├─ .idx(Message Index):消息索引文件
│ ├─ 消息ID
│ ├─ 文件位置
│ └─ 消息大小
│
└─ .ets(Segment File):消息存储文件
└─ 实际的消息内容
文件组织:
msg_store_persistent/(持久化消息)
├─ 0.rdq
├─ 1.rdq
├─ msg_0.ets
├─ msg_1.ets
└─ ...
msg_store_transient/(临时消息)
└─ 同上
5.3.2 消息存储流程
java
// 简化的消息存储流程
public void storeMessage(Message message) {
// 1. 判断消息大小
if (message.size() < EMBED_THRESHOLD) {
// 小消息(< 4KB):直接存储在队列索引
queueIndex.embedMessage(message);
} else {
// 大消息:存储在消息文件
// 1.1 写入消息文件
long offset = messageStore.write(message);
// 1.2 写入索引
messageIndex.write(message.getId(), offset, message.size());
// 1.3 队列索引记录引用
queueIndex.addReference(message.getId());
}
// 2. 如果是持久化消息
if (message.isPersistent()) {
// 异步刷盘
messageStore.flush();
}
}
5.3.3 消息存储优化
优化策略:
1. 批量写入
├─ 多条消息合并写入
└─ 减少磁盘 I/O
2. 异步刷盘
├─ 先写内存缓冲区
├─ 定期(默认每秒)刷盘
└─ 提高吞吐量
3. 内存索引
├─ 消息索引缓存在内存
├─ 快速查找消息位置
└─ 定期持久化
4. 垃圾回收
├─ 定期合并文件
├─ 删除已确认消息
└─ 回收磁盘空间
5. 流量控制
├─ 内存告警(默认 40%)
├─ 磁盘告警(默认 50GB)
└─ 阻塞生产者
5.4 内存管理
内存管理策略:
1. 内存告警(Memory Alarm)
├─ 默认阈值:系统内存的 40%
├─ 触发条件:RabbitMQ 使用内存超过阈值
└─ 行为:阻塞所有生产者连接
2. 内存分配
├─ 连接和信道
├─ 队列进程
├─ 消息(内存队列)
├─ Mnesia 数据库
├─ 插件
└─ 管理界面
3. 内存换页(Paging)
├─ 队列达到一定长度
├─ 消息从内存换到磁盘
├─ 保留消息索引在内存
└─ 消费时再读回内存
内存使用示例:
┌────────────────────────────────────┐
│ RabbitMQ 内存使用(假设 4GB 总内存) │
├────────────────────────────────────┤
│ 1.6GB(40%) ← 内存告警阈值 │
│ │
│ 当前使用:1.2GB │
│ ├─ 连接/信道:200MB │
│ ├─ 队列进程:300MB │
│ ├─ 消息:600MB │
│ └─ 其他:100MB │
└────────────────────────────────────┘
5.5 磁盘管理
磁盘告警(Disk Alarm):
1. 触发条件
├─ 可用磁盘空间 < 阈值(默认 50GB)
└─ 可用磁盘空间 < 磁盘总量的 10%
2. 告警行为
├─ 阻塞所有生产者
├─ 停止接收消息
└─ 允许消费者继续消费
3. 恢复条件
└─ 磁盘空间恢复到阈值以上
磁盘空间分配:
/var/lib/rabbitmq/
├─ mnesia/(元数据,通常很小)
│ └─ 数百 MB
│
└─ msg_stores/(消息存储,主要占用)
└─ 取决于消息量和持久化设置
磁盘清理策略:
├─ 设置消息 TTL(过期自动删除)
├─ 设置队列最大长度
├─ 定期消费消息
└─ 监控磁盘使用
六、数据结构详解
6.1 队列数据结构
erlang
% RabbitMQ 队列内部数据结构(Erlang)
-record(amqqueue, {
name, % 队列名称 {resource, VHost, queue, Name}
durable, % 是否持久化 (true/false)
auto_delete, % 是否自动删除 (true/false)
exclusive_owner, % 独占所有者 (none | connection pid)
arguments, % 参数列表 (list)
pid, % 队列进程 PID
slave_pids, % 镜像队列从节点 PID 列表
sync_slave_pids, % 同步的从节点 PID
policy, % 应用的策略
gm_pids, % 组成员 PID
state % 队列状态
}).
队列内存结构:
Queue Process(队列进程)
│
├─ Message Queue(消息队列,内存)
│ ├─ Unacknowledged Messages(未确认消息)
│ │ └─ {msg_id, delivery_tag, consumer_tag, message}
│ │
│ └─ Ready Messages(待投递消息)
│ └─ FIFO 队列
│
├─ Queue Index(队列索引)
│ ├─ Message Position(消息位置)
│ ├─ Message Status(消息状态)
│ │ ├─ alpha:内存 + 磁盘(消息和索引都在内存)
│ │ ├─ beta:磁盘(消息在磁盘,索引在内存)
│ │ ├─ gamma:磁盘(消息和索引都在磁盘)
│ │ └─ delta:磁盘(批量消息)
│ └─ Sequence Number(序列号)
│
└─ Backing Queue(后备队列,实现)
└─ rabbit_variable_queue(默认实现)
6.2 消息数据结构
erlang
% 消息结构
-record(basic_message, {
exchange_name, % 交换机名称 {resource, VHost, exchange, Name}
routing_keys, % 路由键列表 [binary()]
content, % 消息内容
id, % 消息 ID (binary())
is_persistent % 是否持久化 (boolean())
}).
% 消息内容
-record(content, {
class_id, % 类型 ID (60 = basic)
properties, % 消息属性
properties_bin, % 二进制属性
payload_fragments_rev % 消息体片段(反序)
}).
% 消息属性
-record('P_basic', {
content_type, % 内容类型 (<<"application/json">>)
content_encoding, % 编码 (<<"UTF-8">>)
headers, % 自定义头 [{binary(), any()}]
delivery_mode, % 投递模式 (1 | 2)
priority, % 优先级 (0-9)
correlation_id, % 关联 ID (binary())
reply_to, % 回复队列 (binary())
expiration, % 过期时间 (binary())
message_id, % 消息 ID (binary())
timestamp, % 时间戳 (integer())
type, % 类型 (binary())
user_id, % 用户 ID (binary())
app_id % 应用 ID (binary())
}).
6.3 交换机数据结构
erlang
% 交换机结构
-record(exchange, {
name, % 交换机名称 {resource, VHost, exchange, Name}
type, % 类型 (direct | topic | fanout | headers)
durable, % 是否持久化 (true/false)
auto_delete, % 是否自动删除 (true/false)
internal, % 是否内部使用 (true/false)
arguments, % 参数列表 (list)
policy % 应用的策略
}).
% 绑定结构
-record(binding, {
source, % 源交换机 {resource, VHost, exchange, Name}
key, % 路由键 (binary())
destination, % 目标队列/交换机 {resource, VHost, queue/exchange, Name}
args % 绑定参数 (list)
}).
6.4 索引数据结构
队列索引文件(.idx):
┌─────────────────────────────────────────┐
│ Queue Index File │
├─────────────────────────────────────────┤
│ Segment 1 (16 MB) │
│ ├─ Entry 1 │
│ │ ├─ Sequence Number: 1 │
│ │ ├─ Message ID: abc123 │
│ │ ├─ Status: published │
│ │ ├─ Offset: 0 │
│ │ └─ Size: 1024 bytes │
│ │ │
│ ├─ Entry 2 │
│ │ ├─ Sequence Number: 2 │
│ │ └─ ... │
│ └─ ... │
│ │
│ Segment 2 (16 MB) │
│ └─ ... │
└─────────────────────────────────────────┘
索引项(Index Entry)结构:
struct IndexEntry {
uint64_t seq_id; // 序列号
uint32_t msg_id_hash; // 消息 ID 哈希
uint8_t status; // 状态(0=发布, 1=投递, 2=确认)
uint64_t offset; // 消息在文件中的偏移量
uint32_t size; // 消息大小
uint8_t flags; // 标志位
};
6.5 消息存储文件结构
消息存储文件(.ets):
┌─────────────────────────────────────────┐
│ Message Store File (Segment) │
├─────────────────────────────────────────┤
│ File Header │
│ ├─ Magic Number: 0x524D5153 (RMQS) │
│ ├─ Version: 1 │
│ ├─ Segment ID: 0 │
│ └─ Timestamp: ... │
├─────────────────────────────────────────┤
│ Message 1 │
│ ├─ Message Header │
│ │ ├─ Total Size: 1024 │
│ │ ├─ Message ID: abc123 │
│ │ ├─ Exchange: order.exchange │
│ │ ├─ Routing Key: order.created │
│ │ └─ Properties: {...} │
│ │ │
│ ├─ Message Body │
│ │ └─ Actual payload bytes │
│ └─ Checksum: CRC32 │
├─────────────────────────────────────────┤
│ Message 2 │
│ └─ ... │
└─────────────────────────────────────────┘
文件特性:
├─ 每个 Segment 默认 16MB
├─ 顺序写入(高效)
├─ 批量刷盘(减少 I/O)
└─ 定期合并(垃圾回收)
七、工作模式详解
7.1 简单模式(Simple Queue)
简单模式:
Producer ─────> [Queue] ─────> Consumer
特点:
├─ 一对一
├─ 最简单的模式
└─ 不使用交换机(使用默认交换机)
场景:
└─ 简单的任务队列
代码示例:
java
// 生产者
channel.queueDeclare("simple.queue", true, false, false, null);
channel.basicPublish("", "simple.queue", null, message.getBytes());
// 消费者
channel.basicConsume("simple.queue", true, (tag, message) -> {
System.out.println("收到消息: " + new String(message.getBody()));
});
7.2 工作队列模式(Work Queue)
工作队列模式:
┌──> Consumer 1
│
Producer ─> [Queue]──> Consumer 2
│
└──> Consumer 3
特点:
├─ 一对多
├─ 轮询分发(Round-Robin)
├─ 任务分配
└─ 提高处理能力
场景:
├─ 任务分发
└─ 负载均衡
消息分发:
├─ 默认:轮询(每个消费者均匀分配)
└─ 公平分发:prefetchCount=1(按能力分配)
代码示例:
java
// 生产者发送 10 条消息
for (int i = 1; i <= 10; i++) {
channel.basicPublish("", "work.queue", null, ("Task " + i).getBytes());
}
// 消费者 1(慢)
channel.basicQos(1); // 公平分发
channel.basicConsume("work.queue", false, (tag, message) -> {
Thread.sleep(2000); // 模拟慢处理
System.out.println("消费者1: " + new String(message.getBody()));
channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
});
// 消费者 2(快)
channel.basicQos(1);
channel.basicConsume("work.queue", false, (tag, message) -> {
Thread.sleep(500); // 模拟快处理
System.out.println("消费者2: " + new String(message.getBody()));
channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
});
// 结果:
// 消费者2处理更多消息(公平分发)
7.3 发布/订阅模式(Publish/Subscribe)
发布/订阅模式(Fanout Exchange):
┌──> [Queue 1] ──> Consumer 1
│
Producer ──> [Fanout]──> [Queue 2] ──> Consumer 2
Exchange │
└──> [Queue 3] ──> Consumer 3
特点:
├─ 广播消息
├─ 所有队列都收到消息
└─ 忽略 routing key
场景:
├─ 消息广播
└─ 多系统通知
代码示例:
java
// 声明交换机
channel.exchangeDeclare("logs.fanout", BuiltinExchangeType.FANOUT, true);
// 生产者发送消息
channel.basicPublish("logs.fanout", "", null, "日志消息".getBytes());
// 消费者 1(日志存储)
String queue1 = channel.queueDeclare().getQueue(); // 临时队列
channel.queueBind(queue1, "logs.fanout", "");
channel.basicConsume(queue1, true, (tag, msg) -> {
System.out.println("存储日志: " + new String(msg.getBody()));
});
// 消费者 2(日志打印)
String queue2 = channel.queueDeclare().getQueue();
channel.queueBind(queue2, "logs.fanout", "");
channel.basicConsume(queue2, true, (tag, msg) -> {
System.out.println("打印日志: " + new String(msg.getBody()));
});
7.4 路由模式(Routing)
路由模式(Direct Exchange):
routing key: error
┌──────────────────────> [Error Queue] ──> Consumer 1
│
│ routing key: warning
Producer ──> [Direct]──────────────────────> [Warning Queue] ──> Consumer 2
Exchange │
│ routing key: info
└──────────────────────> [Info Queue] ──> Consumer 3
特点:
├─ 精确匹配 routing key
├─ 选择性接收消息
└─ 点对点路由
场景:
├─ 日志分级
└─ 任务分类
代码示例:
java
// 声明交换机
channel.exchangeDeclare("logs.direct", BuiltinExchangeType.DIRECT, true);
// 生产者发送不同级别日志
channel.basicPublish("logs.direct", "error", null, "错误日志".getBytes());
channel.basicPublish("logs.direct", "warning", null, "警告日志".getBytes());
channel.basicPublish("logs.direct", "info", null, "信息日志".getBytes());
// 消费者 1(只接收 error)
channel.queueBind("error.queue", "logs.direct", "error");
channel.basicConsume("error.queue", true, (tag, msg) -> {
System.out.println("错误: " + new String(msg.getBody()));
});
// 消费者 2(接收 error 和 warning)
channel.queueBind("important.queue", "logs.direct", "error");
channel.queueBind("important.queue", "logs.direct", "warning");
channel.basicConsume("important.queue", true, (tag, msg) -> {
System.out.println("重要: " + new String(msg.getBody()));
});
7.5 主题模式(Topic)
主题模式(Topic Exchange):
pattern: *.error.*
┌──────────────────────> [Error Queue]
│
│ pattern: order.#
Producer ──> [Topic]─┼──────────────────────> [Order Queue]
Exchange │
│ pattern: #.warning
└──────────────────────> [Warning Queue]
特点:
├─ 模式匹配 routing key
├─ * 匹配一个单词
├─ # 匹配零个或多个单词
└─ 灵活路由
场景:
├─ 复杂路由
└─ 多维度分类
代码示例:
java
// 声明交换机
channel.exchangeDeclare("logs.topic", BuiltinExchangeType.TOPIC, true);
// 生产者发送消息
channel.basicPublish("logs.topic", "order.created.info", null, "订单创建".getBytes());
channel.basicPublish("logs.topic", "order.paid.warning", null, "订单支付警告".getBytes());
channel.basicPublish("logs.topic", "user.login.error", null, "用户登录错误".getBytes());
// 消费者 1(所有订单消息:order.#)
channel.queueBind("order.queue", "logs.topic", "order.#");
// 接收:order.created.info, order.paid.warning
// 消费者 2(所有错误消息:*.*.error)
channel.queueBind("error.queue", "logs.topic", "*.*.error");
// 接收:user.login.error
// 消费者 3(所有警告消息:#.warning)
channel.queueBind("warning.queue", "logs.topic", "#.warning");
// 接收:order.paid.warning
7.6 RPC 模式
RPC 模式(请求/响应):
Client ──[1] Request──────> [Request Queue] ──> Server
│
Client <─[4] Response─────< [Reply Queue] <─[3] Process
│ │
└────[2] Wait──────────────────────────────────[3]
特点:
├─ 同步调用
├─ 客户端等待响应
├─ 使用 correlation_id 关联请求和响应
└─ 使用 reply_to 指定响应队列
流程:
1. 客户端发送请求(指定 reply_to 和 correlation_id)
2. 客户端监听响应队列
3. 服务端处理请求,发送响应到 reply_to
4. 客户端接收响应(匹配 correlation_id)
代码示例:
java
// RPC 客户端
public String call(String message) throws Exception {
// 1. 生成关联 ID
String corrId = UUID.randomUUID().toString();
// 2. 创建临时响应队列
String replyQueue = channel.queueDeclare().getQueue();
// 3. 发送请求
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.correlationId(corrId)
.replyTo(replyQueue)
.build();
channel.basicPublish("", "rpc.queue", props, message.getBytes());
// 4. 等待响应
CompletableFuture<String> response = new CompletableFuture<>();
channel.basicConsume(replyQueue, true, (tag, msg) -> {
if (msg.getProperties().getCorrelationId().equals(corrId)) {
response.complete(new String(msg.getBody()));
}
});
return response.get(5, TimeUnit.SECONDS);
}
// RPC 服务端
channel.basicConsume("rpc.queue", false, (tag, msg) -> {
// 1. 处理请求
String request = new String(msg.getBody());
String result = process(request);
// 2. 发送响应
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.correlationId(msg.getProperties().getCorrelationId())
.build();
channel.basicPublish("", msg.getProperties().getReplyTo(), props, result.getBytes());
// 3. 确认消息
channel.basicAck(msg.getEnvelope().getDeliveryTag(), false);
});
八、使用指南
8.1 Java 原生客户端
8.1.1 Maven 依赖
xml
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.20.0</version>
</dependency>
8.1.2 生产者示例
java
import com.rabbitmq.client.*;
public class Producer {
private static final String EXCHANGE_NAME = "order.exchange";
private static final String ROUTING_KEY = "order.created";
public static void main(String[] args) throws Exception {
// 1. 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");
// 2. 创建连接
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// 3. 声明交换机
channel.exchangeDeclare(
EXCHANGE_NAME,
BuiltinExchangeType.DIRECT,
true, // durable
false, // autoDelete
null // arguments
);
// 4. 声明队列
channel.queueDeclare(
"order.queue",
true, // durable
false, // exclusive
false, // autoDelete
null // arguments
);
// 5. 绑定队列
channel.queueBind("order.queue", EXCHANGE_NAME, ROUTING_KEY);
// 6. 发送消息
String message = "订单创建: Order-12345";
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.contentType("text/plain")
.deliveryMode(2) // 持久化
.priority(5)
.timestamp(new Date())
.build();
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, props, message.getBytes());
System.out.println("发送消息: " + message);
}
}
}
8.1.3 消费者示例
java
public class Consumer {
private static final String QUEUE_NAME = "order.queue";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 设置 QoS(每次只取1条消息)
channel.basicQos(1);
// 消费消息(手动确认)
DeliverCallback deliverCallback = (consumerTag, message) -> {
try {
String msg = new String(message.getBody(), "UTF-8");
System.out.println("收到消息: " + msg);
// 模拟业务处理
Thread.sleep(1000);
// 手动确认
channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
} catch (Exception e) {
// 拒绝消息,重新入队
channel.basicNack(
message.getEnvelope().getDeliveryTag(),
false, // multiple
true // requeue
);
}
};
channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {});
System.out.println("等待消息...");
}
}
8.2 Spring Boot 集成
8.2.1 Maven 依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
8.2.2 配置文件
yaml
# application.yml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
# 发布者确认
publisher-confirm-type: correlated
publisher-returns: true
# 消费者配置
listener:
simple:
acknowledge-mode: manual # 手动确认
prefetch: 1 # 预取数量
retry:
enabled: true
max-attempts: 3
8.2.3 配置类
java
@Configuration
public class RabbitMQConfig {
// 声明交换机
@Bean
public DirectExchange orderExchange() {
return new DirectExchange("order.exchange", true, false);
}
// 声明队列
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
.ttl(60000) // 消息 TTL
.maxLength(10000) // 最大长度
.deadLetterExchange("dlx.exchange") // 死信交换机
.build();
}
// 绑定
@Bean
public Binding orderBinding(Queue orderQueue, DirectExchange orderExchange) {
return BindingBuilder.bind(orderQueue)
.to(orderExchange)
.with("order.created");
}
// RabbitTemplate 配置
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
// 消息发送确认
template.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
System.out.println("消息发送成功");
} else {
System.err.println("消息发送失败: " + cause);
}
});
// 消息返回(路由失败)
template.setReturnsCallback(returned -> {
System.err.println("消息路由失败: " + returned.getMessage());
});
return template;
}
}
8.2.4 生产者
java
@Service
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void createOrder(Order order) {
// 1. 保存订单
saveOrder(order);
// 2. 发送消息
rabbitTemplate.convertAndSend(
"order.exchange",
"order.created",
order,
message -> {
// 设置消息属性
message.getMessageProperties().setContentType("application/json");
message.getMessageProperties().setPriority(5);
return message;
}
);
}
}
8.2.5 消费者
java
@Component
public class OrderListener {
@RabbitListener(queues = "order.queue")
public void handleOrder(Order order, Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// 业务处理
System.out.println("处理订单: " + order);
// 模拟处理
processOrder(order);
// 手动确认
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
// 拒绝消息,重新入队
channel.basicNack(deliveryTag, false, true);
}
}
// 使用注解声明队列和绑定
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "payment.queue", durable = "true"),
exchange = @Exchange(value = "order.exchange", type = "direct"),
key = "order.paid"
))
public void handlePayment(Order order) {
System.out.println("处理支付: " + order);
}
}
九、高级特性
9.1 消息可靠性保证
9.1.1 生产者确认
java
// 1. 启用发布者确认
channel.confirmSelect();
// 2. 发送消息
channel.basicPublish(exchange, routingKey, props, body);
// 3. 等待确认
if (channel.waitForConfirms()) {
System.out.println("消息发送成功");
} else {
System.out.println("消息发送失败");
}
// 批量确认
for (int i = 0; i < 100; i++) {
channel.basicPublish(exchange, routingKey, props, body);
}
channel.waitForConfirmsOrDie(5000); // 等待所有确认
// 异步确认(推荐)
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) {
System.out.println("消息确认: " + deliveryTag);
}
@Override
public void handleNack(long deliveryTag, boolean multiple) {
System.out.println("消息拒绝: " + deliveryTag);
// 重发消息
}
});
9.1.2 消息持久化
java
// 1. 交换机持久化
channel.exchangeDeclare(exchange, type, true); // durable=true
// 2. 队列持久化
channel.queueDeclare(queue, true, false, false, null); // durable=true
// 3. 消息持久化
AMQP.BasicProperties props = MessageProperties.PERSISTENT_TEXT_PLAIN;
// 或
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.deliveryMode(2) // 2=持久化
.build();
channel.basicPublish(exchange, routingKey, props, body);
9.1.3 消息补偿机制
java
@Service
public class MessageService {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private MessageRepository messageRepository;
// 发送消息(带补偿)
public void sendMessage(String content) {
// 1. 保存消息到数据库
Message msg = new Message();
msg.setContent(content);
msg.setStatus("PENDING");
messageRepository.save(msg);
try {
// 2. 发送消息
rabbitTemplate.convertAndSend("exchange", "routingKey", content);
// 3. 标记为已发送
msg.setStatus("SENT");
messageRepository.save(msg);
} catch (Exception e) {
// 4. 标记为失败
msg.setStatus("FAILED");
messageRepository.save(msg);
}
}
// 定时补偿
@Scheduled(fixedDelay = 60000)
public void retryFailedMessages() {
List<Message> failed = messageRepository.findByStatus("FAILED");
for (Message msg : failed) {
try {
rabbitTemplate.convertAndSend("exchange", "routingKey", msg.getContent());
msg.setStatus("SENT");
messageRepository.save(msg);
} catch (Exception e) {
// 继续失败
}
}
}
}
9.2 死信队列(DLX)
死信队列场景:
1. 消息被拒绝(basic.reject / basic.nack)且 requeue=false
2. 消息 TTL 过期
3. 队列达到最大长度
死信队列架构:
正常流程:
Producer ──> [Normal Exchange] ──> [Normal Queue] ──> Consumer
│
消息变成死信
│
死信流程: ↓
[Dead Letter Exchange]
│
↓
[Dead Letter Queue] ──> DLX Consumer
配置示例:
java
@Configuration
public class DLXConfig {
// 1. 死信交换机
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange("dlx.exchange", true, false);
}
// 2. 死信队列
@Bean
public Queue dlxQueue() {
return new Queue("dlx.queue", true);
}
// 3. 死信绑定
@Bean
public Binding dlxBinding() {
return BindingBuilder.bind(dlxQueue())
.to(dlxExchange())
.with("dlx");
}
// 4. 正常队列(配置死信交换机)
@Bean
public Queue normalQueue() {
return QueueBuilder.durable("normal.queue")
.deadLetterExchange("dlx.exchange") // 指定死信交换机
.deadLetterRoutingKey("dlx") // 死信路由键
.ttl(60000) // 消息 TTL 60 秒
.maxLength(1000) // 最大长度
.build();
}
}
死信消费者:
java
@Component
public class DLXListener {
@RabbitListener(queues = "dlx.queue")
public void handleDeadLetter(Message message) {
// 分析死信原因
Map<String, Object> headers = message.getMessageProperties().getHeaders();
String reason = (String) headers.get("x-first-death-reason");
System.out.println("死信原因: " + reason);
System.out.println("原交换机: " + headers.get("x-first-death-exchange"));
System.out.println("原路由键: " + headers.get("x-first-death-queue"));
// 记录日志、告警、补偿处理
}
}
9.3 延迟队列
延迟队列实现方案:
方案1:TTL + 死信队列
┌──────────────────────────────────────────────┐
│ Producer ──> [TTL Queue] ──TTL过期──> [DLX] │
│ (无消费者) │
│ │ │
│ └──> [Delay Queue] ──> Consumer
└──────────────────────────────────────────────┘
方案2:延迟队列插件(推荐)
└─ rabbitmq-delayed-message-exchange 插件
方案1:TTL + 死信队列:
java
@Configuration
public class DelayQueueConfig {
// 1. 延迟交换机
@Bean
public DirectExchange delayExchange() {
return new DirectExchange("delay.exchange");
}
// 2. TTL 队列(无消费者,消息过期后进入死信队列)
@Bean
public Queue ttlQueue() {
return QueueBuilder.durable("ttl.queue")
.ttl(30000) // 30 秒后过期
.deadLetterExchange("real.exchange")
.deadLetterRoutingKey("real")
.build();
}
// 3. 真正的队列
@Bean
public Queue realQueue() {
return new Queue("real.queue", true);
}
@Bean
public DirectExchange realExchange() {
return new DirectExchange("real.exchange");
}
@Bean
public Binding realBinding() {
return BindingBuilder.bind(realQueue())
.to(realExchange())
.with("real");
}
}
// 发送延迟消息
rabbitTemplate.convertAndSend("delay.exchange", "delay", "延迟30秒的消息");
// 30秒后,消息自动到达 real.queue
@RabbitListener(queues = "real.queue")
public void handle(String message) {
System.out.println("收到延迟消息: " + message);
}
方案2:延迟队列插件:
bash
# 安装插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
java
@Bean
public CustomExchange delayExchange() {
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange(
"delayed.exchange",
"x-delayed-message", // 类型
true,
false,
args
);
}
// 发送延迟消息
rabbitTemplate.convertAndSend("delayed.exchange", "delayed", "消息", message -> {
message.getMessageProperties().setDelay(30000); // 延迟 30 秒
return message;
});
9.4 优先级队列
java
@Bean
public Queue priorityQueue() {
return QueueBuilder.durable("priority.queue")
.maxPriority(10) // 最大优先级 10
.build();
}
// 发送高优先级消息
rabbitTemplate.convertAndSend("exchange", "priority", "重要消息", message -> {
message.getMessageProperties().setPriority(9);
return message;
});
// 发送低优先级消息
rabbitTemplate.convertAndSend("exchange", "priority", "普通消息", message -> {
message.getMessageProperties().setPriority(1);
return message;
});
// 消费顺序:优先级高的先消费
9.5 集群与高可用
9.5.1 集群模式
RabbitMQ 集群架构:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ │ │ │ │ │
│ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │
│ │ Queue │ │ │ │ Queue │ │ │ │ Queue │ │
│ │ A │ │ │ │ B │ │ │ │ C │ │
│ └────────┘ │ │ └────────┘ │ │ └────────┘ │
│ │ │ │ │ │
│ Metadata │───│ Metadata │───│ Metadata │
│ (Mnesia) │ │ (Mnesia) │ │ (Mnesia) │
└──────────────┘ └──────────────┘ └──────────────┘
特点:
├─ 元数据共享(Mnesia 数据库同步)
├─ 队列不共享(每个队列在一个节点)
└─ 消息不复制(普通集群)
搭建集群:
bash
# 节点 1
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
# 节点 2(加入节点 1)
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@node1
rabbitmqctl start_app
# 节点 3(加入节点 1)
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@node1
rabbitmqctl start_app
# 查看集群状态
rabbitmqctl cluster_status
9.5.2 镜像队列(高可用)
镜像队列架构:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Master │ │ Mirror 1 │ │ Mirror 2 │
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ │ │ │ │ │
│ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │
│ │ Queue │──┼───┼─>│ Queue │──┼───┼─>│ Queue │ │
│ │ A │<─┼───┼──│ A' │<─┼───┼──│ A'' │ │
│ │(Master)│ │ │ │(Mirror)│ │ │ │(Mirror)│ │
│ └────────┘ │ │ └────────┘ │ │ └────────┘ │
└──────────────┘ └──────────────┘ └──────────────┘
特点:
├─ 消息自动同步到镜像节点
├─ Master 故障,Mirror 自动升级
└─ 保证消息不丢失
配置镜像队列:
bash
# 方式1:命令行配置 Policy
rabbitmqctl set_policy ha-all "^ha\." \
'{"ha-mode":"all","ha-sync-mode":"automatic"}'
# 参数说明:
# ha-mode:
# - all:镜像到所有节点
# - exactly:镜像到指定数量节点 {"ha-mode":"exactly","ha-params":2}
# - nodes:镜像到指定节点 {"ha-mode":"nodes","ha-params":["rabbit@node1","rabbit@node2"]}
# ha-sync-mode:
# - manual:手动同步
# - automatic:自动同步(推荐)
java
// 方式2:Java 代码配置
@Bean
public Queue haQueue() {
return QueueBuilder.durable("ha.queue")
.build();
}
// 在 RabbitMQ 管理界面配置 Policy
// Pattern: ^ha\.
// Apply to: queues
// Definition: {"ha-mode":"all","ha-sync-mode":"automatic"}
十、性能优化与最佳实践
10.1 性能优化
10.1.1 批量发送
java
// ❌ 不推荐:逐条发送
for (int i = 0; i < 10000; i++) {
rabbitTemplate.convertAndSend("exchange", "key", "消息" + i);
}
// ✅ 推荐:批量发送
List<Message> messages = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
messages.add(new Message(("消息" + i).getBytes()));
}
// 分批发送(每批 100 条)
for (int i = 0; i < messages.size(); i += 100) {
List<Message> batch = messages.subList(i, Math.min(i + 100, messages.size()));
// 批量发送
}
10.1.2 连接和信道复用
java
// ❌ 不推荐:每次创建新连接
public void send() throws Exception {
Connection conn = factory.newConnection();
Channel channel = conn.createChannel();
channel.basicPublish(...);
channel.close();
conn.close(); // 频繁创建/关闭连接,性能差
}
// ✅ 推荐:复用连接和信道
private static Connection connection;
private static ThreadLocal<Channel> channelThreadLocal = new ThreadLocal<>();
static {
try {
connection = factory.newConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
public void send() throws Exception {
Channel channel = channelThreadLocal.get();
if (channel == null || !channel.isOpen()) {
channel = connection.createChannel();
channelThreadLocal.set(channel);
}
channel.basicPublish(...);
}
10.1.3 预取数量(prefetch)
java
// 设置预取数量
channel.basicQos(10); // 每次预取 10 条消息
// 场景:
// prefetch=1:公平分发,适合处理时间不均匀的任务
// prefetch=10-50:提高吞吐量,适合处理时间均匀的任务
// prefetch=0:无限制(不推荐)
10.1.4 消息压缩
java
// 发送前压缩
public void sendCompressed(String message) throws Exception {
byte[] compressed = compress(message.getBytes());
rabbitTemplate.convertAndSend("exchange", "key", compressed);
}
// 接收后解压
@RabbitListener(queues = "queue")
public void receive(byte[] compressed) throws Exception {
String message = new String(decompress(compressed));
System.out.println(message);
}
private byte[] compress(byte[] data) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(baos);
gzip.write(data);
gzip.close();
return baos.toByteArray();
}
private byte[] decompress(byte[] data) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
GZIPInputStream gzip = new GZIPInputStream(bais);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = gzip.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
gzip.close();
return baos.toByteArray();
}
10.2 最佳实践
10.2.1 命名规范
交换机命名:
├─ {业务}.{类型}.exchange
└─ 示例:order.direct.exchange、user.topic.exchange
队列命名:
├─ {业务}.{功能}.queue
└─ 示例:order.create.queue、user.register.queue
路由键命名:
├─ {业务}.{动作}.{级别}
└─ 示例:order.created.info、user.login.error
10.2.2 消息幂等性
java
// 方案1:消息 ID 去重
@Service
public class MessageService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@RabbitListener(queues = "order.queue")
public void handleOrder(Order order, Message message) {
String messageId = message.getMessageProperties().getMessageId();
// 检查是否已处理
Boolean exists = redisTemplate.opsForValue()
.setIfAbsent("msg:" + messageId, "1", 24, TimeUnit.HOURS);
if (Boolean.TRUE.equals(exists)) {
// 第一次处理
processOrder(order);
} else {
// 重复消息,忽略
System.out.println("重复消息: " + messageId);
}
}
}
// 方案2:业务唯一键去重
@RabbitListener(queues = "order.queue")
public void handleOrder(Order order) {
try {
// 使用数据库唯一索引保证幂等
orderRepository.insert(order); // order_id 唯一索引
} catch (DuplicateKeyException e) {
// 重复订单,忽略
System.out.println("重复订单: " + order.getId());
}
}
10.2.3 监控告警
yaml
# application.yml
management:
endpoints:
web:
exposure:
include: '*'
metrics:
export:
prometheus:
enabled: true
spring:
rabbitmq:
listener:
simple:
retry:
enabled: true
max-attempts: 3
initial-interval: 1000
java
// 自定义监控
@Component
public class RabbitMQMonitor {
@Autowired
private RabbitTemplate rabbitTemplate;
@Scheduled(fixedDelay = 60000)
public void monitorQueues() {
Properties props = rabbitTemplate.execute(channel -> {
AMQP.Queue.DeclareOk ok = channel.queueDeclarePassive("order.queue");
Properties p = new Properties();
p.setProperty("messageCount", String.valueOf(ok.getMessageCount()));
p.setProperty("consumerCount", String.valueOf(ok.getConsumerCount()));
return p;
});
int messageCount = Integer.parseInt(props.getProperty("messageCount"));
if (messageCount > 10000) {
// 告警:消息积压
alert("队列消息积压: " + messageCount);
}
}
}
10.2.4 消息顺序性
保证消息顺序的方案:
方案1:单队列 + 单消费者
Producer ──> [Queue] ──> Consumer (prefetch=1)
问题:吞吐量低
方案2:分区队列
Producer ──hash(orderId)──> [Queue 1] ──> Consumer 1
├──> [Queue 2] ──> Consumer 2
└──> [Queue 3] ──> Consumer 3
好处:同一 orderId 的消息进入同一队列,保证顺序
方案3:业务层保证
├─ 使用版本号
└─ 使用时间戳
java
// 方案2:分区队列示例
public void sendOrder(Order order) {
// 根据订单 ID 哈希,选择队列
int partition = Math.abs(order.getId().hashCode() % 3);
String queue = "order.queue." + partition;
rabbitTemplate.convertAndSend("order.exchange", queue, order);
}
// 每个队列一个消费者
@RabbitListener(queues = "order.queue.0", concurrency = "1")
public void handle0(Order order) { ... }
@RabbitListener(queues = "order.queue.1", concurrency = "1")
public void handle1(Order order) { ... }
@RabbitListener(queues = "order.queue.2", concurrency = "1")
public void handle2(Order order) { ... }