bigywater.tech
RabbitMQ · 3

默认 exchange 与队列名路由

教程里“直接往队列发消息”其实是发给一个名字为空的 exchange。它是什么、有哪些限制、写错队列名会怎样、会比显式 exchange 更快吗?这篇一项项在 RabbitMQ 4.3.6 上验证。

403
对默认 exchange 做删除、绑定、解绑、重新声明,全部是 ACCESS_REFUSED
255 字节
队列名与 routing key 的长度上限(AMQP 短字符串)
545 vs 536 ms
20000 条消息走默认 exchange 与显式 direct exchange 的耗时中位数,差别小于波动

它是什么

实测 · 本机执行输出
  $ rabbitmqctl list_queues name messages
    Timeout: 60.0 seconds ...
    Listing queues for vhost / ...
    name	messages
    hello	1
  basic_get → ['hi']
  $ rabbitmqctl list_bindings source_name destination_name routing_key
    Listing bindings for vhost /...
    source_name	destination_name	routing_key
    	hello	hello
  list_bindings 里的第一列为空串,就是默认 exchange;这个绑定不是我建的,队列一声明就有

默认 exchange 的名字是空串,direct 类型。每个队列声明时 broker 自动给它加一个以队列名为 key 的绑定,所以 basic_publish(exchange='', routing_key=队列名) 就等于“直接发到这个队列”。这个绑定在 list_bindings 里能看到,删除队列时跟着消失。

发给不存在的队列

实测 · 本机执行输出
  不加 mandatory:发布没有任何错误,消息也没有去哪(没有这个队列)
  mandatory=True + confirm: UnroutableError: 1 unroutable message(s) returned
  对存在的队列 mandatory=True: 成功

routing key 对不上任何队列时,消息被悄悄丢弃,发布方没有错误。要得到反馈需要 mandatory 加 publisher confirm(得到 UnroutableError)。

被保护

实测 · 本机执行输出
  exchange_delete(""):         ChannelClosedByBroker: (403, 'ACCESS_REFUSED - operation not permitted on the default exchange')
  queue_bind("hello", "", k):  ChannelClosedByBroker: (403, 'ACCESS_REFUSED - operation not permitted on the default exchange')
  queue_unbind(…, ""):         ChannelClosedByBroker: (403, 'ACCESS_REFUSED - operation not permitted on the default exchange')
  exchange_declare("", fanout): ChannelClosedByBroker: (403, 'ACCESS_REFUSED - operation not permitted on the default exchange')
  exchange_declare("amq.foo"):  ChannelClosedByBroker: (403, "ACCESS_REFUSED - exchange name 'amq.foo' contains reserved prefix 'amq.*'")
  管理 API 里它叫 amq.default: {'durable': True, 'internal': False, 'name': '', 'type': 'direct'}

不能删除、绑定、解绑或重新声明默认 exchange(403 ACCESS_REFUSED);自己声明的 exchange 名字也不能以 amq. 开头。管理 API 里默认 exchange 的名字是 amq.default。

队列名

实测 · 本机执行输出
  queue_declare("") → 服务器生成名字 amq.gen-…(共 30 个字符,前缀 amq.gen-)
  用生成的名字作 routing key 发布 → ['to-generated']
  队列 'a.b.c': 通过默认 exchange 收到 ['x']
  队列 'with space': 通过默认 exchange 收到 ['x']
  队列 '中文队列': 通过默认 exchange 收到 ['x']
  队列 'UPPER': 通过默认 exchange 收到 ['x']
  255 字节的名字: 成功
  256 字节的名字: ShortStringTooLong(pika 在客户端就拒绝了,字符串超过 255 字节,没有发给 broker)
  routing key 256 字节: ShortStringTooLong(pika 在客户端就拒绝了,字符串超过 255 字节,没有发给 broker)
  list_bindings 里有没有 a.b.c 那一行:删除队列前 True,删除后 False
  $ rabbitmqctl list_bindings source_name destination_name routing_key
    Listing bindings for vhost /...
    source_name	destination_name	routing_key
    	UPPER	UPPER
    	hello	hello
    	with space	with space
    	中文队列	中文队列

空队列名由 broker 生成 amq.gen-…;中文、空格、大写都可以;名字和 routing key 都是 AMQP 短字符串,上限 255 字节,超出时 pika 在客户端就拒绝。

性能

实测 · 本机执行输出
  默认 exchange:中位数 545 ms(最小 532、最大 672);显式 direct exchange:中位数 536 ms(最小 530、最大 685)

20000 条消息走默认 exchange 与走显式 direct exchange 耗时一样(中位数 545 ms 与 536 ms,轮间波动更大)。默认 exchange 的好处是少两步声明,不是更快。

分步演示

默认 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,数字每次运行会有小幅抖动。mandatory 的实验开了 confirm_delivery;速度对比不开 confirm,两种方式交替跑 7 轮,每轮 purge 后重发,最后一次 queue_declare(passive) 往返确保 broker 处理完
    • 没有验证:集群里队列在别的节点时默认 exchange 的转发;quorum 队列和 stream 队列通过默认 exchange 的行为(第 15、17 篇会涉及队列类型);非 ASCII 队列名的编码边界(UTF-8 字节数);vhost 隔离下的默认 exchange;消息体更大时的速度差异
    京ICP备2026067502号-1京公网安备11010502063487号