阶段一建立了 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.#能匹配order、order.payment、order.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=pdf 和 region=cn 的消息才会被投递。
headers 的适用场景是需要按多个非路由键属性过滤,且这些属性不适合塞进 RoutingKey 的点号结构。比如消息按「格式 + 区域 + 类型」三维过滤,用 topic 表达会很别扭(RoutingKey 要拼成一长串约定),用 headers 更自然。
但 headers 用得少有现实原因:匹配性能比其他三种差(要遍历 headers),且调试不直观(RoutingKey 一眼能看懂,headers 要展开看)。除非场景真的需要多维度属性过滤,一般用 topic 就够了。headers 更像是「当 RoutingKey 表达力不够时的补充手段」。
四种 Exchange 的对比与选择
把四种放一起对比,选择框架就清晰了:
| Exchange | 路由依据 | 灵活性 | 性能 | 典型场景 |
|---|---|---|---|---|
| direct | RoutingKey 精确相等 | 低(固定 key) | 高 | 按类型/级别分流 |
| fanout | 忽略 key,全广播 | 无(不可过滤) | 最高 | 纯广播通知 |
| topic | RoutingKey 通配匹配 | 高(模式匹配) | 中高 | 按规则的多级分发 |
| headers | 消息 headers 属性 | 中(多维度) | 中 | 多属性条件过滤 |
选择的判断顺序:
- 要不要过滤? 不要(全收)→ fanout;要 → 继续。
- 过滤维度单一还是多维? 多维且不适合拼成 RoutingKey → headers;单一或可拼成层级 → 继续。
- 匹配规则是固定的还是模式的? 固定 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」。

