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

6.3 KiB
Raw Blame History

tags, create time, update time
tags create time update time
mq/rabbitmq
exchange-routing
direct-exchange
fanout-exchange
topic-exchange
headers-exchange
delay-exchange
2026-08-08 18:50 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 完全一致,消息才会被投递。

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。每次收到消息就是群发。

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.# 是
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 过期
  • 队列长度限制已满
# 队列声明时指定 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 代码示例:声明延迟 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 代码示例:带延迟头的消息
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 天然支持层级化路由,运维上更简洁。

关联笔记