没有插件时,RabbitMQ 做延迟消息靠“TTL + 死信”。这样做能准到多少毫秒?给每条消息单独设 expiration 为什么会翻车?延迟队列要怎么设计,消息躺在里面占多少资源?这篇都量一遍。
发送时刻(ms): [0, 304, 612] 到达时刻(ms): [1002, 1307, 1614];实际延迟(到达 − 发送): [1002, 1003, 1002] ms
消息发到一个没有消费者的延迟队列,队列设 x-message-ttl 和 x-dead-letter-exchange;过期后死信被转到真正的业务队列。实测延迟比设定值只多几毫秒。
classic 队列:到达死信队列的 (时刻 ms, 消息): [(3004, 'long(3000ms)'), (3004, 'short(300ms)')] quorum 队列:到达死信队列的 (时刻 ms, 消息): [(3006, 'long(3000ms)'), (3006, 'short(300ms)')]
队列只在队头检查是否过期。每条消息单独设 expiration 时,TTL 短的排在 TTL 长的后面,就要等前面的先过期。classic 和 quorum 队列都是如此。
到达死信队列的 (时刻 ms, 消息): [(303, 'short(300ms)'), (3003, 'long(3000ms)')]
如果先短后长,各自准时;但发布顺序无法保证,所以不能依赖。
几乎同时发出,到达死信队列的 (时刻 ms, 消息): [(303, 'b(300)'), (303, 'd(300)'), (1003, 'c(1000)'), (1003, 'f(1000)'), (2005, 'a(2000)'), (2005, 'e(2000)')]
每种延迟时间建一个队列、用队列级 TTL,同一队列里 TTL 相同,先进先出就是先过期,没有阻塞。代价是延迟时间只能取预先定义的几档。
收到 200 条;延迟 min 501 / p50 503 / p99 506 / max 506 ms(目标 500)
队列 TTL=500 ms 时延迟实测 501~506 ms。队列压满消息、broker 忙时的精度没有测。
big.q: messages=50000 memory=0.7 MiB message_bytes=48.8 MiB 队列进程自己占的内存很小(消息体在磁盘上),但 50000 条、约 48.8 MiB 的数据要在队列里一直躺 60 秒;没有单独的“延迟存储”
延迟期间消息一直占着队列(磁盘和索引),量大、延迟长的场景要算上这部分。