bigywater.tech
RabbitMQ · 2

四种 exchange:direct、fanout、topic、headers 的路由实测

direct、fanout、topic、headers 四种 exchange 到底按什么规则把消息分给队列?topic 的 # 能不能匹配零个单词,headers 多出一个头会怎样,绑定很多时谁会变慢?这篇每种都建好队列、发出一批 key,把每个队列真实收到的东西列成表。

t.all=10 / t.lazy=5
topic 的 # 全收 10 条;lazy.# 收到 5 条,包括单独的 lazy
3 条 × 3 队列
fanout 不看 routing key:3 条消息每个队列都收到 3 条
3387 ms
headers 绑定数 1000 时发布 10000 条的耗时(direct 276 ms、topic 295 ms)

direct

实测 · 本机执行输出
  d.a        收到 1 条: ["key='red'"]
  d.b        收到 1 条: ["key='blue'"]
  d.c        收到 2 条: ["key='red'", "key='blue'"]
  发布了 5 条(red、blue、green、Red、"red "):green、Red(大小写不同)和 "red "(末尾空格)没有任何绑定,直接丢弃;d.c 绑了两个 key,red 和 blue 各收一条

routing key 与绑定 key 逐字节相等才路由,大小写、空格都算。一个队列可以用多个 key 绑同一个 exchange;没有匹配的消息直接丢弃。

fanout

实测 · 本机执行输出
  f.a        收到 3 条: ["key='anything'", "key=''", "key='k1'"]
  f.b        收到 3 条: ["key='anything'", "key=''", "key='k1'"]
  f.c        收到 3 条: ["key='anything'", "key=''", "key='k1'"]
  3 条消息 × 3 个队列 = 每个队列都收到 3 条,与发布时和绑定时的 routing key 都无关

不看 routing key,所有绑定的队列各收一份。绑定时给的 key 也被忽略。

topic

实测 · 本机执行输出
  routing key               t.orange  t.rabbit  t.lazy    t.all     
  quick.orange.rabbit       ✓         ✓         ·         ✓         
  lazy.orange.elephant      ✓         ·         ✓         ✓         
  quick.orange.fox          ✓         ·         ·         ✓         
  lazy.brown.fox            ·         ·         ✓         ✓         
  lazy.pink.rabbit          ·         ✓         ✓         ✓         
  quick.brown.fox           ·         ·         ·         ✓         
  orange                    ·         ·         ·         ✓         
  quick.orange.male.rabbit  ·         ·         ·         ✓         
  lazy.orange.male.rabbit   ·         ·         ✓         ✓         
  lazy                      ·         ·         ✓         ✓         
  各队列收到的条数: t.orange=3, t.rabbit=2, t.lazy=5, t.all=10

key 是点分的单词。* 匹配恰好一个单词,# 匹配零个或多个单词(所以 lazy.# 也能匹配 lazy 本身)。表里每个 key 对每个绑定的匹配结果都是真实收到的。

headers

实测 · 本机执行输出
  消息头                         h.all     h.any     
  pdf+report                  ✓         ✓         
  pdf+log                     ·         ✓         
  txt+report                  ·         ✓         
  txt only                    ·         ·         
  pdf+report+extra            ✓         ✓         
  无头                          ·         ·         

按消息头匹配,忽略 routing key。x-match=all 要求绑定里列的每个头都匹配,any 只要一个;消息多出来的头不影响匹配。

去重

实测 · 本机执行输出
  u.q        收到 1 条: ['a.b 这一条']
  u.q2       收到 1 条: ['a.b 这一条']
  u.q 有 3 个绑定(a.*、#、a.b)都匹配 a.b,队列里仍只有 1 份

一条消息命中同一个队列的多个绑定,队列里只有一份;命中几个队列就复制几份。

代价

实测 · 本机执行输出
  绑定数 B               10         100        1000
  direct          272 ms      272 ms      276 ms
  topic           277 ms      278 ms      295 ms
  headers         266 ms      426 ms     3387 ms

direct 和 topic 的匹配基本不随绑定数增长;headers 要逐个比较绑定,1000 个绑定时 10000 条消息用了 3387 ms,而 direct 只要 276 ms。

分步演示

四种 exchange
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / RabbitMQ 4.3.6 / Erlang/OTP 28.5 / pika 1.4.4(Homebrew 与临时虚拟环境);所有节点都是本机自己起的 RabbitMQ 进程(127.0.0.1,端口 9500 以上,每个实验独立的数据目录与节点名,不连任何外部服务);命令输出来自真实的 rabbitmqctl 与管理 API,发布与消费用 pika 的真实连接;计时用 time.perf_counter,数字每次运行会有小幅抖动。每一节先 queue_purge 再发布,发布后等 0.2 秒用 basic_get 取空每个队列;第六节发布时不开 publisher confirm,最后用一次 queue_declare(passive) 往返确保 broker 已处理完,3 次取中位数,每次之间 purge
    • 没有验证:有消费者在线时的行为(这里只用 basic_get);exchange-to-exchange 绑定;alternate-exchange;一致性哈希等插件 exchange;绑定数超过 1000 的 topic 性能;消息体更大时的路由开销;集群里 exchange 的元数据同步
    京ICP备2026067502号-1京公网安备11010502063487号