bigywater.tech
RabbitMQ · 12

优先级队列的实现与代价

给消息加 priority 就能让重要的先被处理吗?为什么有时高优先级的消息还是排在后面?开 255 档优先级要付出多少代价?quorum 队列呢?这篇在 RabbitMQ 4.3.6 上逐项验证。

第 21 个
prefetch=20 时后发的高优先级消息被处理的位置(prefetch=1 时是第 2 个)
3.9 倍
x-max-priority=255 比不设优先级时 basic_get 取完 20000 条的耗时
1.1 MB
x-max-priority=255 的空队列占用的内存(不设时 14 KB)

排序规则

实测 · 本机执行输出
  发送顺序: a(p1) b(p5) c(p10) d(p3) e(p0) f(无) g(p7) h(p5)
  取出顺序: ['c(p10)', 'g(p7)', 'b(p5)', 'h(p5)', 'd(p3)', 'a(p1)', 'e(p0)', 'f(pNone)']
  同一优先级内保持先进先出(b 在 h 前);没有优先级的消息按 0 算,在 p0 的 e 之后(它们同级、先进先出)

队列声明时带 x-max-priority,消息带 priority 属性。取出时按优先级降序,同级先进先出,没写优先级的按 0 算。

上限与声明

实测 · 本机执行输出
  max-priority=5,发 a(p5) b(p200) c(p6) d(p1):取出 ['a', 'b', 'c', 'd'](a、b、c 都按 5 算,先进先出)
  普通队列发 a(p1) b(p9) c(p5):取出 ['a', 'b', 'c'](优先级被忽略)

消息优先级超过上限的按上限算;没声明 x-max-priority 的队列完全不看优先级。

预取的局限

实测 · 本机执行输出
  prefetch=20,先发 60 条低优先级(p1)再发 1 条 HIGH(p10);HIGH 是第 21 个被处理的(共 61 条)
  prefetch=1:HIGH 是第 2 个被处理的(共 61 条)

优先级只能重排还在队列里的消息。已经预取给消费者的消息不会被后来的高优先级消息超过,所以要让优先级有意义,消费者的 prefetch 要小。

取出的耗时

实测 · 本机执行输出
  x-max-priority=不设  : 发布 20000 条 510 ms,basic_get 取完 1570 ms
  x-max-priority=10  : 发布 20000 条 517 ms,basic_get 取完 1724 ms
  x-max-priority=255 : 发布 20000 条 1218 ms,basic_get 取完 6108 ms

优先级档数越多,取出越慢:取 20000 条,255 档的耗时是不设优先级时的 3.9 倍,10 档的开销只有一成左右。

内存

实测 · 本机执行输出
  空队列内存(字节): 不设: 14008, max-priority=10: 56840, max-priority=255: 1152088

空队列的内存:不设 14 KB、10 档 57 KB、255 档 1.1 MB,每多一档就多一份内部结构。

quorum 队列

实测 · 本机执行输出
  quorum + x-max-priority=5 → ChannelClosedByBroker: (406, "PRECONDITION_FAILED - invalid arg 'x-max-priority' for queue … of queue type rabbit_quorum_queue")
  依次发优先级 [1, 9, 3, 5, 2, 10, 0, 7, 4, 6, 8, 12, 200, 5, 5]
  quorum 队列取出(序号:优先级): ['12:200', '11:12', '5:10', '1:9', '10:8', '7:7', '9:6', '3:5', '13:5', '14:5', '8:4', '2:3', '4:2', '0:1', '6:0']
  优先级从大到小排好,同级(三个 5)保持先进先出(序号 3、13、14);200 没有被截成 255 以内的某个值(quorum 没有 x-max-priority 这个上限)

声明 quorum 队列时不能带 x-max-priority(406),但消息上的 priority 照样被排序,实测是完整的降序、同级先进先出。(这与我印象中“quorum 只有高低两档”的说法不同,以 4.3.6 实测为准。)

分步演示

优先级队列
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。第一、二、六节用 basic_get 取出观察顺序;第三节消费者每条处理 10 ms,第一个线程先发 60 条低优先级再发 HIGH;第四节 3 轮取中位数,优先级随机(固定随机种子);空队列内存来自 rabbitmqctl list_queues memory
    • 没有验证:有消息时各档的内存占用;多个消费者下的优先级效果;优先级与 TTL / 死信 / 长度限制组合;stream 队列没有优先级;quorum 队列优先级的内部实现与性能代价;更长时间运行后的稳定性
    京ICP备2026067502号-1京公网安备11010502063487号