Skip to content

RabbitMQ交换机和路由

RabbitMQ 中生产者并不直接发送消息到 Queue,而是发送到 Exchange。Exchange 根据自己的类型、routing key、binding key 决定消息应该进入哪些队列。

为什么路由设计很重要

Exchange 是 RabbitMQ 的“分发器”。同一条消息进入哪个队列,取决于交换机类型和绑定规则。如果路由键设计混乱,短期能跑,后期会出现三个问题:生产者不知道该发什么 routing key,消费者不知道自己为什么收到某类消息,排查时也很难判断消息到底应该进哪个队列。

路由匹配原理

mermaid
flowchart TD
    A["Producer 发布消息"] --> B["Exchange 收到 routing key"]
    B --> C["读取绑定关系 Binding"]
    C --> D{"交换机类型"}
    D -- "direct" --> E["完全匹配 binding key"]
    D -- "fanout" --> F["忽略 routing key<br/>投递所有绑定队列"]
    D -- "topic" --> G["按 * 和 # 通配符匹配"]
    E --> H["写入匹配队列"]
    F --> H
    G --> H

direct 交换机

direct 交换机要求 routing key 和 binding key 完全一致。

mermaid
flowchart TD
    P["Producer: order.created"] --> E["Direct Exchange"]
    E -->|order.created| Q1["创建订单队列"]
    E -->|order.paid| Q2["支付订单队列"]

适合明确的一对一路由。例如 order.created 只进入创建订单队列,order.paid 只进入支付订单队列。

fanout 交换机

fanout 交换机会忽略 routing key,把消息广播给所有绑定队列。

mermaid
flowchart TD
    P["Producer"] --> E["Fanout Exchange"]
    E --> Q1["短信队列"]
    E --> Q2["邮件队列"]
    E --> Q3["站内信队列"]

适合广播事件,例如“系统配置刷新”“用户注册成功通知多个系统”。

topic 交换机

topic 交换机使用通配符匹配 routing key。

  • * 匹配一个单词。
  • # 匹配零个或多个单词。
mermaid
flowchart TD
    P["Producer: order.pay.success"] --> E["Topic Exchange"]
    E -->|order.*.success| Q1["订单成功事件队列"]
    E -->|order.#| Q2["所有订单事件队列"]
    E -->|*.pay.*| Q3["支付相关队列"]

适合多维度订阅。例如订单、支付、物流都可以按业务域、动作、结果组合 routing key。

headers 交换机

headers 交换机根据消息头匹配,不依赖 routing key。它灵活但性能和可读性不如 direct/topic,业务中较少使用。

路由设计建议

  1. routing key 使用业务语义,不要使用无意义编号。
  2. 单词之间用点分隔,例如 order.pay.success
  3. direct 适合明确路由,topic 适合分类订阅,fanout 适合广播。
  4. 不要让一个 Exchange 承担所有业务域,容易导致绑定关系混乱。
  5. 重要消息要配合发布确认和死信队列。

如果不会设计 routing key,常见后果是:一个队列收到太多不相关消息,只能在消费者代码里再次判断;或者一个业务事件要复制发送多次,生产者和消费者耦合越来越重。好的 routing key 应该让“消息属于哪个业务域、发生了什么动作、结果是什么”一眼可见。

和 RocketMQ Topic 的区别

RocketMQ 通常是生产者发送到 Topic,消费者订阅 Topic。RabbitMQ 则多了一层 Exchange 路由。

mermaid
flowchart TD
    A[RocketMQ] --> B[Producer -> Topic -> ConsumerGroup]
    C[RabbitMQ] --> D[Producer -> Exchange -> Queue -> Consumer]

RocketMQ 的模型更适合高吞吐主题订阅;RabbitMQ 的 Exchange 模型在复杂路由上更直观。

配置 Demo:声明交换机、队列和绑定

下面用 Spring AMQP 声明一个 topic 交换机和两个队列。

java
@Configuration
public class RabbitConfig {

    @Bean
    TopicExchange orderExchange() {
        return new TopicExchange("order.exchange", true, false);
    }

    @Bean
    Queue paidQueue() {
        return QueueBuilder.durable("order.paid.queue").build();
    }

    @Bean
    Queue allOrderQueue() {
        return QueueBuilder.durable("order.all.queue").build();
    }

    @Bean
    Binding paidBinding() {
        return BindingBuilder.bind(paidQueue())
            .to(orderExchange())
            .with("order.pay.success");
    }

    @Bean
    Binding allOrderBinding() {
        return BindingBuilder.bind(allOrderQueue())
            .to(orderExchange())
            .with("order.#");
    }
}

order.pay.success 只进入支付成功队列,order.# 会匹配所有订单事件。

常见风险

风险为什么会发生处理方式
消息无法路由routing key 和 binding key 不匹配开启 mandatory 和 return callback
fanout 被滥用广播到所有队列,消费者收到无关消息只用于确实需要广播的事件
topic 规则过宽# 匹配太多消息优先使用明确的业务前缀
一个 Exchange 混所有业务绑定关系越来越乱按业务域拆分 Exchange
队列未持久化Broker 重启后队列丢失声明 durable queue/exchange