阶段一建立了 AMQP 模型和可靠性的地基,阶段二进入 RabbitMQ 最有特色的部分——路由。而路由的全部秘密,都藏在 Exchange(交换器) 这个角色里。

RabbitMQ 内置四种 Exchange 类型:direct、fanout、topic、headers。很多人把它们记成「四种配置选项」,用的时候随便选一个。这种用法浪费了 RabbitMQ 最大的优势——它的四种 Exchange 不是平行的备选项,而是四种不同的路由语义模型,各自解决一类分发问题。选对 Exchange,路由拓扑简洁清晰;选错,要么过度复杂,要么功能不够。

这一篇把四种 Exchange 的路由规则、适用场景、常见误用讲清楚,给一个能落地的选择框架。

direct:精确匹配的定向投递

direct Exchange 的路由规则最简单:消息的 RoutingKey 必须和 Binding 的 BindingKey 完全相等,消息才会被投递到该队列

比如一个 direct Exchange,队列 A 用 BindingKey info 绑定,队列 B 用 error 绑定。生产者发 RoutingKey=info 的消息,只有队列 A 收到;发 error 的,只有队列 B 收到;发 debug 的(没有对应绑定),消息无处可去(会被丢弃或 return)。

direct 的特点是精确、可控。它适合「按固定类型分流」的场景:日志按级别(info/warn/error)分流到不同处理方,或者按业务类型分流(order/payment/refund)。每条消息的去向是确定性的,便于追踪。

direct 的一个常见用法是一个 Exchange 绑多个相同 BindingKey 的队列。比如两个队列都用 info 绑定同一个 direct Exchange,那么一条 info 消息会被复制到两个队列——这其实就实现了「按 key 的发布订阅」。所以 direct 既能做点对点分流,也能做有限的广播,灵活性比想象的高。

fanout:忽略 key 的广播

fanout Exchange 的规则最粗暴:忽略 RoutingKey,消息被投递到所有绑定的队列

生产者发消息时甚至不用指定 RoutingKey(指定了也被忽略)。Exchange 把消息复制一份给每个绑定的队列。如果有 5 个队列绑过来,一条消息就变成 5 份。

fanout 适合纯广播场景:一个事件需要通知所有相关方,且每个方都要完整处理。比如系统配置变更广播——配置中心发一条「配置已更新」,所有服务节点都要重新拉取配置,每个节点都该收到。

fanout 是发布订阅模型最直接的实现。它的缺点是不灵活——无法过滤,所有绑定的队列无差别全收。如果下游只需要其中一部分消息,用 fanout 会让下游收到大量无关消息再自己过滤,浪费带宽和处理。这种「需要选择性接收」的场景,该用 topic。

topic:通配符的模式匹配

topic Exchange 是 RabbitMQ 路由能力的精华。它的规则是:RoutingKey 和带通配符的 BindingKey 做模式匹配

topic 的 RoutingKey 和 BindingKey 都是用点号 . 分隔的多段字符串,比如 order.payment.success。支持的通配符:

  • *:匹配恰好一段。order.*.success 能匹配 order.payment.success,但不能匹配 order.payment.vip.success(多了一段)。
  • #:匹配零段或多段。order.# 能匹配 orderorder.paymentorder.payment.success.vip

这种模式匹配能力,让 topic 能表达非常灵活的路由规则:

  • 队列 A 绑 order.*.success:收所有订单成功事件,不管哪种订单。
  • 队列 B 绑 order.payment.#:收所有支付相关事件,成功失败都要。
  • 队列 C 绑 order.refund.#:收所有退款事件。
  • 队列 D 绑 #.error:收所有业务线的错误事件(全局错误监控)。

topic 是 RabbitMQ 区别于 Kafka 的核心。Kafka 的 topic 是「订阅一整类」,而 topic Exchange 能「按规则订阅某一类里的子集」。需要按租户、按业务线、按状态做精细化分发的场景,topic Exchange 几乎是唯一的好选择。

topic 的两个特殊边界:BindingKey 是 # 时,匹配所有消息(退化成 fanout);BindingKey 不含通配符时,退化为精确匹配(等价于 direct)。所以 topic 在某种意义上是 direct 和 fanout 的超集,功能最强,但匹配开销也略高(每次投递都要做模式匹配)。

headers:按消息头的属性匹配

headers Exchange 是四种里用得最少的。它的规则是:忽略 RoutingKey,根据消息的 headers 属性匹配

绑定时指定一个 headers 字典和一个匹配模式(all/any)。消息的 headers 满足这个条件就投递:

  • x-match=all:所有指定的 header 键值对都要匹配。
  • x-match=any:任意一个匹配即可。

比如绑定 {x-match: all, format: pdf, region: cn},只有 headers 同时包含 format=pdfregion=cn 的消息才会被投递。

headers 的适用场景是需要按多个非路由键属性过滤,且这些属性不适合塞进 RoutingKey 的点号结构。比如消息按「格式 + 区域 + 类型」三维过滤,用 topic 表达会很别扭(RoutingKey 要拼成一长串约定),用 headers 更自然。

但 headers 用得少有现实原因:匹配性能比其他三种差(要遍历 headers),且调试不直观(RoutingKey 一眼能看懂,headers 要展开看)。除非场景真的需要多维度属性过滤,一般用 topic 就够了。headers 更像是「当 RoutingKey 表达力不够时的补充手段」。

四种 Exchange 的对比与选择

把四种放一起对比,选择框架就清晰了:

Exchange 路由依据 灵活性 性能 典型场景
direct RoutingKey 精确相等 低(固定 key) 按类型/级别分流
fanout 忽略 key,全广播 无(不可过滤) 最高 纯广播通知
topic RoutingKey 通配匹配 高(模式匹配) 中高 按规则的多级分发
headers 消息 headers 属性 中(多维度) 多属性条件过滤

选择的判断顺序:

  1. 要不要过滤? 不要(全收)→ fanout;要 → 继续。
  2. 过滤维度单一还是多维? 多维且不适合拼成 RoutingKey → headers;单一或可拼成层级 → 继续。
  3. 匹配规则是固定的还是模式的? 固定 key → direct;需要通配符 → topic。

实际项目里,topic 用得最多,因为它能覆盖大多数「按业务层级 + 通配」的分发需求。direct 用于简单分流,fanout 用于广播,headers 几乎只在特殊场景出现。

一个常见误用:用 fanout 替代 topic

新手常犯的错误:本意要「按类型选择性接收」,却用了 fanout,结果每个下游都收到全部消息,再在消费端自己判断类型、丢弃不需要的。这种做法的问题:

  • 浪费带宽和 CPU:下游要接收并解析所有消息,再扔掉大部分。
  • 扩展性差:加一个新类型,所有下游都要改过滤逻辑。
  • 失去了 MQ 路由的意义——MQ 本来就是帮你做过滤的,自己再做一遍是重复劳动。

正确做法是把这类「选择性接收」需求交给 topic Exchange,让 MQ 在投递时就过滤好,下游只收到自己真正关心的消息。这就是「把路由决策放到 MQ 层」的设计原则。

四种交换器的路由语义对比

收束:选对 Exchange 是路由设计的第一步

四种 Exchange 是 RabbitMQ 路由能力的具体实现。选对 Exchange,路由拓扑就成功了一半。下一篇会深入 topic 的细节——RoutingKey 和 BindingKey 怎么设计、通配符怎么用、点号层级怎么规划,那才是路由设计的真正功夫。


关于十三Tech

我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。我相信 AI 是程序员的最佳搭档。想跟完这套「图解 RabbitMQ」,欢迎关注公众号 「十三Tech」

十三Tech公众号二维码