prefetch 到底限制什么?global 标志有什么用?太小和太大各会怎样?这篇先数 ready / unacked 看清含义,再在 loopback 和加了 10 ms 往返的链路上分别量吞吐,最后看消费者崩溃时的代价。
消费者收到了 5 条;(ready, unacked) = (15, 5)
ack 了第 1 条之后又收到 1 条(总共 6);(ready, unacked) = (14, 5)
再 ack 到最后一条(multiple=True)之后共收到 11 条;(ready, unacked) = (9, 5)
$ rabbitmqctl list_channels prefetch_count messages_unacknowledged consumer_count
Listing channels ...
prefetch_count messages_unacknowledged consumer_count
0 0 0
5 5 1basic.qos(prefetch_count=N) 让 broker 给每个消费者最多推 N 条未确认的消息;每确认一条才再推一条。它限制的是在途(unacked)数,不是总数。
list_deprecated_features(摘取): classic_queue_mirroring=removed/denied, global_qos=denied_by_default/denied, transient_nonexcl_queues=denied_by_default/denied
默认配置 global=False: 两个消费者各收到 {'a': 5, 'b': 5},合计 10;(ready, unacked) = (30, 10)
默认配置 global=True: 两个消费者各收到 {'a': 5, 'b': 5},合计 10;(ready, unacked) = (30, 10)
打开 permit.global_qos 后 global=True: 两个消费者各收到 {'a': 5, 'b': 0},合计 5;(ready, unacked) = (35, 5)
默认配置下 global=True 没有报错,效果和 global=False 一样(每个消费者各 5);只有显式打开 permit.global_qos,两个消费者才共享 5AMQP 里 global=True 本来表示整个 channel 共享这个限制。4.3.6 把 global_qos 列为 denied_by_default 的废弃特性:默认配置下请求不报错,但效果和 global=False 一样(每消费者各 N);显式打开 deprecated_features.permit.global_qos 才共享。
prefetch=5 + auto_ack=True:收到 1000 条;(ready, unacked) = (0, 0)
auto_ack 的消费者没有未确认状态,prefetch 对它不起作用,1000 条一次推完。
prefetch 耗时 吞吐 1 2058 ms 9718 条/秒 10 674 ms 29665 条/秒 100 635 ms 31520 条/秒 1000 576 ms 34711 条/秒 0(无限) 527 ms 37944 条/秒
prefetch=1 因为每条一次往返只有 9718 条/秒,提到 10 后 29665 条/秒,再提到 1000 也只有 34711 条/秒,收益递减。
prefetch 耗时 吞吐 理论上限(= prefetch ÷ 往返 10 ms) 1 25706 ms 78 条/秒 100 条/秒 10 2639 ms 758 条/秒 1000 条/秒 100 258 ms 7761 条/秒 10000 条/秒 1000 148 ms 13488 条/秒 100000 条/秒
往返变成 10 ms 之后差距是量级的:吞吐 ≈ prefetch ÷ 往返时间(实测 78、758、7761、13488 条/秒)。跨机房的消费者不能用 prefetch=1。
消费者在线时 (ready, unacked) = (4000, 1000) 消费者断开之后 (ready, unacked) = (5000, 0) 把 5000 条全取出来,其中 redelivered=True 的有 1000 条(正好是被推给那个崩溃消费者的 1000 条)
prefetch 越大,一个消费者攥着的在途消息越多:它崩溃时这些消息全部重投,慢的消费者还会把消息“囤”在手里不让别人处理。