Files
autumn-recruitment/04.MQ/rabbitmq/Exchange 路由机制.md
T

171 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [mq/rabbitmq, exchange-routing, direct-exchange, fanout-exchange, topic-exchange, headers-exchange, delay-exchange]
create time: 2026-08-08 18:50
update time: 2026-08-08 18:50
---
# Exchange 路由机制
## 概述
Exchange(交换机)是 RabbitMQ 消息路由的核心组件,充当生产者与队列之间的中间层。生产者在发送消息时指定 Exchange 名称和 routing key,RabbitMQ 根据 Exchange 类型和路由规则将消息分发到一个或多个 Queue。掌握四种内置 Exchange 类型以及插件扩展的延迟交换能力,是设计可靠消息系统的基础。
## 核心原理
### 四种内置 Exchange 类型
#### Direct Exchange — 精确匹配
Direct Exchange 使用完全匹配原则:routing key 必须与 Queue 绑定到 Exchange 时的 binding key 完全一致,消息才会被投递。
```mermaid
sequenceDiagram
participant P as 生产者
participant E["Direct Exchange"]
participant Q1["Queue A (key: order.create)"]
participant Q2["Queue B (key: order.* )"]
participant Q3["Queue C (key: *.pay)"]
P->>E: 发送消息<br/>routing key: "order.create"
E->>Q1: 匹配成功,投递
Note over E,Q1: "order.create" == "order.create"
E->>Q2: 匹配失败
Note over E,Q2: "order.create" ne "order.*"
E->>Q3: 匹配失败
Note over E,Q3: "order.create" ne "*.pay"
```
关键特征:一对一或一对多投递(多个 Queue 绑定了相同的 binding key)。这是最常用也最可预测的路由方式。
#### Fanout Exchange — 广播模式
Fanout Exchange 忽略 routing key,将消息投递到所有绑定到该 Exchange 的 Queue。每次收到消息就是群发。
```mermaid
sequenceDiagram
participant P as 生产者
participant E["Fanout Exchange"]
participant Q1["订单队列"]
participant Q2["日志队列"]
participant Q3["通知队列"]
P->>E: 发送消息<br/>routing key: (任意值)
E->>Q1: 广播投递
E->>Q2: 广播投递
E->>Q3: 广播投递
Note right of E: 忽略 routing key<br/>全部投递
```
典型场景:事件广播、配置刷新、缓存失效通知。不需要关心消息内容被哪些消费者处理。
#### Topic Exchange — 通配符匹配
Topic Exchange 是最灵活的路由模式,支持通配符匹配:
- `*`(星号)匹配**恰好一个词**
- `#`(井号)匹配**零个或多个词**
词之间以点号 `.` 分隔。匹配规则示例:
| routing key | binding key | 是否匹配 |
|-------------|------------|---------|
| `quick.orange.fox` | `*.orange.*` | 是 |
| `quick.orange.fox` | `quick.*.fox` | 是 |
| `quick.brown.fox` | `*.orange.*` | 否 |
| `quick.orange.male.rabbit` | `#` | 是(匹配所有) |
| `lazy.orange.cat` | `lazy.#` | 是 |
```mermaid
sequenceDiagram
participant P as 生产者
participant E["Topic Exchange"]
participant Q1["Queue: *.create"]
participant Q2["Queue: *.pay.*"]
participant Q3["Queue: #"]
P->>E: routing key: "order.create"
E->>Q1: 匹配 "*" -> 投递
E->>Q2: "order.pay.*" ne "*.pay.*" -> 不投递
E->>Q3: "#" -> 投递
P->>E: routing key: "order.pay.success"
E->>Q1: "*.create" ne "order.pay.success" -> 不投递
E->>Q2: 匹配 "*.pay.*" -> 投递
E->>Q3: "#" -> 投递
```
#### Headers Exchange — 基于属性匹配
Headers Exchange 忽略 routing key,通过检查消息的 headers 属性(key-value 对)来决定路由。可以设置 `match` 参数:`all`(所有 header 都匹配)或 `any`(任一 header 匹配即可)。
> [!WARNING]
> Headers Exchange 性能较差且灵活性不如 Topic Exchange,官方文档明确标注"不推荐在生产中使用"。应优先选择 Topic Exchange。
### 死信交换机的特殊绑定
Dead Letter Exchange(DLX)本质上也是一个普通 Exchange,只是通过队列属性间接触发。当队列中的消息满足以下条件之一时,会被重新路由到 DLX:
- 消息被 nack 且不重新入队(`requeue=false`)
- 消息 TTL 过期
- 队列长度限制已满
```yaml
# 队列声明时指定 DLX
queue:
name: "order.processing"
arguments:
x-dead-letter-exchange: "dead-letter-exchange"
x-dead-letter-routing-key: "order.dead"
```
### 延迟交换插件(x-delayed-message)
RabbitMQ 本身不提供延迟消息功能,需要安装 `rabbitmq_delayed_message_exchange` 插件后使用自定义 Exchange 类型 `x-delayed-message`。它通过消息的 `x-delay` header 控制投递时机。
```go
// Go 代码示例:声明延迟 Exchange
// (amqp.go 库简化写法)
args := amqp.Table{
"x-delayed-type": "direct", // 内部转发使用的 Exchange 类型
}
ch.ExchangeDeclare(
"delayed.exchange", // name
"x-delayed-message", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // args
args, // 自定义参数
)
```
构建消息时设置 `x-delay` header(单位毫秒):
```go
// Go 代码示例:带延迟头的消息
msg := amqp.Publishing{
Body: []byte(`{"orderId":"123"}`),
Headers: amqp.Table{
"x-delay": uint32(60000), // 延迟 60 秒
},
}
```
> [!TIP]
> 面试常考点:RabbitMQ 原生不支持延迟消息。常见替代方案有定时任务轮询、Redis ZSet 定时弹出、或使用 Kafka 的分区时间排序特性。但在高可靠性场景中,x-delayed-message 插件是最直接可靠的方案。
## 实践场景
| 场景 | 推荐 Exchange | 原因 |
|------|--------------|-----|
| 订单创建通知下游 | Direct | 精确控制消息去向 |
| 全局缓存失效广播 | Fanout | 所有服务实例都需要收到 |
| 日志分级收集(info/error/warn) | Topic | 按级别前缀灵活路由 |
| 支付回调不同金额段分流 | Topic | 按 `amount.linux``amount.high` 等维度拆分 |
| 超时未支付取消订单 | x-delayed-message | 精确控制延迟投递时间 |
| 死信消息归档 | Direct + DLX 属性 | 标准化异常流程处理 |
> [!TIP]
> 秋招面试技巧:当被问到"如何设计一个支持多种消息类型的路由系统"时,优先考虑 Topic Exchange 的通配符能力,而非为每种类型创建独立的 Direct Exchange。Topic Exchange 天然支持层级化路由,运维上更简洁。
## 关联笔记
- [[消息持久化与可靠性投递]]
- [[ACK 确认与死信队列]]
- [[推拉结合消费模式]]