我们这个文章,主体对象是对rabbitmq的逻辑实体去进行全面的认识和介绍。
什么是rabbitmq,这个其实本质上就是,本体就是一个可以存储大量json数据的队列。也就是queue。本质上,我认为他的主体就是这个。
但是,由于使用erlang语言编写的,他可以短时间承受大量的json请求接收。
这个我认为就是这个最重要的point。
但是为了适应更领灵活的场景,于是,有多个交换机,多个队列。也提供了几个基本的机制,来处理问题。
一、本体:队列,就是队列
别绕弯子。
RabbitMQ 的本体,就是一个能存消息的队列(Queue)。
生产者往里塞
消费者从里取
先进先出,天然解耦
消息体是什么?可以是 JSON,可以是二进制,但在绝大多数业务场景里,它就是 JSON。订单、日志、事件通知......全是 JSON 往队列里扔。
所以你说"存储大量 JSON 数据的队列",从业务视角看,没毛病。这就是开发者每天打交道的真实模样。
二、硬核底座:Erlang,扛住瞬时洪峰
为什么这个队列能扛住高并发?
因为它是用 Erlang 写的。
轻量级进程模型:每条连接、每个队列都是独立进程,互不干扰
调度器直接跑在 BEAM 虚拟机上,上下文切换成本极低
天生为"高并发、低延迟、软实时"场景而生
结果是什么?
瞬间涌入 10 万条 JSON 消息,RabbitMQ 能稳稳接住,不崩、不丢、不卡死。
这才是它立住的根本。没有这个底座,后面所有"灵活机制"都是空谈。
好,直接上干货。按你的风格:不绕弯,抓本质,讲场景。
一、交换机:消息的"分拣员"
交换机就干一件事:接收消息,决定往哪个队列扔。
就这么简单。但因为业务场景不同,所以有了 4 种分拣策略。
1. Direct Exchange ------ 精准投递
设计逻辑:一对一,精确匹配 Routing Key。
怎么玩:
java
// 生产者
channel.basicPublish("order_exchange", "order.create", null, message.getBytes());
// 绑定
channel.queueBind("order_create_queue", "order_exchange", "order.create");
channel.queueBind("order_cancel_queue", "order_exchange", "order.cancel");
场景:
- 订单创建 → 进
order.create队列 - 订单取消 → 进
order.cancel队列 - 支付成功 → 进
payment.success队列
一句话 :点对点,谁的消息进谁的队列。
2. Fanout Exchange ------ 广播分发
设计逻辑 :不管你叫啥,绑了就发。无视 Routing Key。
怎么玩:
java
// 生产者
channel.basicPublish("log_exchange", "", null, logMessage.getBytes());
// Routing Key 写空都行,反正不看
// 绑定(多个队列)
channel.queueBind("log_queue_1", "log_exchange", "");
channel.queueBind("log_queue_2", "log_exchange", "");
channel.queueBind("log_queue_3", "log_exchange", "");
场景:
- 系统日志 → 同时发给日志服务、监控服务、审计服务
- 用户注册事件 → 同时通知邮件服务、短信服务、积分服务
- 配置变更 → 广播给所有微服务实例
一句话 :一发多收,谁绑了谁拿。
3. Topic Exchange ------ 模糊匹配
设计逻辑 :Routing Key 支持通配符,* 匹配一个单词,# 匹配多个单词。
怎么玩:
java
// Routing Key 格式:order.create.us / order.cancel.cn / payment.success
// 绑定
channel.queueBind("us_order_queue", "topic_exchange", "order.*.us");
channel.queueBind("all_order_queue", "topic_exchange", "order.#");
channel.queueBind("payment_queue", "topic_exchange", "payment.*");
场景:
order.create.us→ 美国订单队列order.create.cn→ 中国订单队列order.#→ 所有订单(不管哪个国家、哪个操作)payment.success/payment.fail→ 支付相关队列
一句话 :多维度分类,灵活路由。
4. Headers Exchange ------ 属性匹配
设计逻辑:不看 Routing Key,看消息头(headers)里的属性。
怎么玩:
java
// 消息头
Map<String, Object> headers = new HashMap<>();
headers.put("type", "order");
headers.put("region", "us");
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.headers(headers)
.build();
channel.basicPublish("headers_exchange", "", props, message.getBytes());
// 绑定(匹配 headers)
Map<String, Object> bindingArgs = new HashMap<>();
bindingArgs.put("x-match", "all"); // all=全匹配,any=任一匹配
bindingArgs.put("type", "order");
bindingArgs.put("region", "us");
channel.queueBind("us_order_queue", "headers_exchange", "", bindingArgs);
场景:
- 消息需要多个维度筛选(如:类型=订单 AND 地区=美国)
- Routing Key 太复杂,不如用 headers 灵活定义
一句话 :用消息头做路由条件,适合复杂匹配。
交换机对比表
| 交换机类型 | 匹配方式 | 灵活性 | 典型场景 |
|---|---|---|---|
| Direct | 精确匹配 Routing Key | 低 | 点对点任务分发 |
| Fanout | 无视 Routing Key,全广播 | 最低 | 日志、事件通知 |
| Topic | 通配符匹配 Routing Key | 高 | 多维度消息分类 |
| Headers | 匹配消息头属性 | 最高 | 复杂路由条件 |
二、队列:消息的"仓库"
队列的本质就一个:存消息,等消费。
但为了应对不同场景,队列有了不同的"特性配置"。
1. 普通队列 ------ 默认形态
特性:
- 非持久化(服务重启就清空)
- 无 TTL(消息永不过期)
- 无优先级
场景:
- 临时任务
- 测试环境
- 不重要的通知
一句话 :最简单,用完就扔。
2. 持久化队列 ------ 保命配置
特性:
java
// 声明队列时设置 durable=true
channel.queueDeclare("order_queue", true, false, false, null);
- 队列元数据持久化(服务重启,队列还在)
- 消息也可以持久化(发布时设置
MessageProperties.PERSISTENT_TEXT_PLAIN)
场景:
- 订单数据
- 支付信息
- 任何不能丢的消息
一句话 :服务挂了,消息还在。
3. 优先级队列 ------ 插队机制
特性:
java
// 声明队列时设置最大优先级
Map<String, Object> args = new HashMap<>();
args.put("x-max-priority", 10); // 优先级范围 0-10
channel.queueDeclare("priority_queue", true, false, false, args);
// 发布消息时设置优先级
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.priority(9) // 优先级越高,越先被消费
.build();
场景:
- VIP 用户订单优先处理
- 紧急任务插队
- 系统告警优先推送
一句话 :重要的消息,先处理。
4. 死信队列(DLQ) ------ 垃圾回收站
特性:
- 正常队列处理失败的消息,自动转入死信队列
- 触发条件:
- 消息被拒绝(
basic.reject/basic.nack) - 消息 TTL 过期
- 队列达到最大长度
- 消息被拒绝(
java
// 声明正常队列,绑定死信队列
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dlx_exchange");
args.put("x-dead-letter-routing-key", "dead_letter");
channel.queueDeclare("normal_queue", true, false, false, args);
场景:
- 消息处理失败,需要人工介入
- 超时未处理的消息归档
- 异常消息追踪与重试
一句话 :处理不了的,先扔一边,别堵着。
5. 延迟队列 ------ 定时炸弹
特性:
- 需要安装插件:
rabbitmq_delayed_message_exchange - 消息发布时设置延迟时间,到期后才投递到队列
java
// 声明延迟交换机
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
channel.exchangeDeclare("delay_exchange", "x-delayed-message", true, false, args);
// 发布延迟消息
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.headers(Collections.singletonMap("x-delay", 60000)) // 延迟 60 秒
.build();
channel.basicPublish("delay_exchange", "delay_queue", props, message.getBytes());
场景:
- 订单 30 分钟未支付自动取消
- 预约提醒(提前 1 小时通知)
- 定时任务调度
一句话 :到点再处理,不用自己写定时器。
6. 镜像队列 ------ 高可用保障
特性:
- 队列数据在集群多个节点间同步
- 主节点挂了,从节点自动接管
bash
# 通过策略设置镜像
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
场景:
- 金融交易系统
- 核心业务队列
- 不能容忍单点故障的场景
一句话 :一个挂了,还有备份。
队列特性对比表
| 队列类型 | 核心特性 | 适用场景 |
|---|---|---|
| 普通队列 | 非持久化,简单 | 临时任务、测试 |
| 持久化队列 | 服务重启不丢数据 | 订单、支付等核心业务 |
| 优先级队列 | 消息可插队 | VIP 任务、紧急告警 |
| 死信队列 | 处理失败消息归档 | 异常追踪、重试机制 |
| 延迟队列 | 消息延迟投递 | 订单超时、定时提醒 |
| 镜像队列 | 多节点数据同步 | 高可用、金融级系统 |
三、为什么这么设计?
一句话总结:
交换机解决"往哪发",队列解决"怎么存"。
交换机的设计逻辑:
| 问题 | 解决方案 |
|---|---|
| 消息太多,不能都进一个队列 | 用 Direct 精准分流 |
| 一个消息要通知多个服务 | 用 Fanout 广播 |
| 消息维度多,需要灵活分类 | 用 Topic 通配符匹配 |
| 路由条件复杂,Routing Key 不够用 | 用 Headers 属性匹配 |
队列的设计逻辑:
| 问题 | 解决方案 |
|---|---|
| 服务挂了,消息不能丢 | 持久化队列 |
| 重要消息要优先处理 | 优先级队列 |
| 处理失败的消息不能堵着 | 死信队列 |
| 需要延迟处理 | 延迟队列 |
| 不能容忍单点故障 | 镜像队列 |
四、完整示例:电商系统消息架构
java
// ====== 交换机声明 ======
// Topic 交换机:处理订单相关消息
channel.exchangeDeclare("order_topic_exchange", "topic");
// Fanout 交换机:广播日志
channel.exchangeDeclare("log_fanout_exchange", "fanout");
// ====== 队列声明 ======
// 持久化队列:订单创建
channel.queueDeclare("order_create_queue", true, false, false, null);
channel.queueBind("order_create_queue", "order_topic_exchange", "order.create.*");
// 持久化队列:订单取消
channel.queueDeclare("order_cancel_queue", true, false, false, null);
channel.queueBind("order_cancel_queue", "order_topic_exchange", "order.cancel.*");
// 死信队列配置
Map<String, Object> dlqArgs = new HashMap<>();
dlqArgs.put("x-dead-letter-exchange", "dlx_exchange");
dlqArgs.put("x-message-ttl", 60000); // 1分钟超时
channel.queueDeclare("order_process_queue", true, false, false, dlqArgs);
// 延迟队列:30分钟未支付自动取消
Map<String, Object> delayArgs = new HashMap<>();
delayArgs.put("x-delayed-type", "direct");
channel.exchangeDeclare("delay_exchange", "x-delayed-message", true, false, delayArgs);
channel.queueDeclare("order_timeout_queue", true, false, false, null);
channel.queueBind("order_timeout_queue", "delay_exchange", "order.timeout");
// ====== 发布消息 ======
// 订单创建
channel.basicPublish("order_topic_exchange", "order.create.us",
MessageProperties.PERSISTENT_TEXT_PLAIN,
orderJson.getBytes());
// 延迟消息:30分钟后检查是否支付
AMQP.BasicProperties delayProps = new AMQP.BasicProperties.Builder()
.headers(Collections.singletonMap("x-delay", 1800000)) // 30分钟
.build();
channel.basicPublish("delay_exchange", "order.timeout", delayProps, orderId.getBytes());
五、一句话收尾
交换机是路由规则,队列是存储策略 。
4 种交换机应对 4 种分发需求,6 种队列特性应对 6 种存储场景。
组合起来,就能覆盖 99% 的业务需求。
需要我把这部分扩展成完整章节,或者加更多实际代码示例吗?