使用过程中发现,延迟消息没有效果,消息直接就被消费了的情况。因为不是所有的消息都出现了没有延迟消息效果的因素,通过有问题的消息特征,大致猜测可能是延迟时间过长导致了消息延迟失败。
问题定位
对之前的延迟消息使用样例接口做一下微改,增加了一个请求参数delay来控制延迟时间:
@GetMapping("/delay/message")public String delayMessageMaxTime(@RequestParam Long delayTime, @RequestParam String message){String send = message + " delay: " + delayTime;log.info("Send: " + send);testTopic.output().send(MessageBuilder.withPayload(send).setHeader("x-delay",delayTime).setHeader("version","1.0").build());return send;}
然后尝试发起了两个请求:
请求1:延迟5000毫秒。消息发送到MQ之后确实延迟了5秒之后才得到了消费,没有任何问题。
2021-07-09 09:56:28.349 INFO 11692 --- [ctor-http-nio-2] c.s.RouterMqApplication$TestController : Send: hello delay: 50002021-07-09 09:56:33.391 INFO 11692 --- [ontent-router-1] c.snow.RouterMqApplication$TestListener : Received v1 : hello delay: 5000, 1.0
请求2:延迟1年(31536000000毫秒)。消息发送到MQ之后马上就被消费者消费了,完全没有延迟效果。
2021-07-09 09:57:29.409 INFO 11692 --- [ctor-http-nio-2] c.s.RouterMqApplication$TestController : Send: hello delay: 315360000002021-07-09 09:57:29.411 INFO 11692 --- [ontent-router-1] c.snow.RouterMqApplication$TestListener : Received v1 : hello delay: 31536000000, 1.0
问题小结
在明确了问题原因之后,需要对该功能的时候做一些明确的限定(延迟时间的极限),以避免再次出现类似的问题。深入探索下去,这里的失败主要与消息的过期时间(TTL)有直接的关系。在RabbitMQ中,消息的过期时间必须是非负 32 位整数,即:0 <= n <= 2^32-1,以毫秒为单位。 其中,2^32-1 = 4294967295。
这里我们可以尝试下面两个请求,分别设置延迟时间为4294967295何4294967296:
2021-07-09 09:58:27.111 INFO 11692 --- [ctor-http-nio-2] c.s.RouterMqApplication$TestController : Send: hello delay: 42949672952021-07-09 09:58:51.933 INFO 11692 --- [ctor-http-nio-2] c.s.RouterMqApplication$TestController : Send: hello delay: 42949672962021-07-09 09:58:51.935 INFO 11692 --- [ontent-router-1] c.snow.RouterMqApplication$TestListener : Received v1 : hello delay: 4294967296, 1.0
可以发现,当延迟时间为4294967295毫秒的时候,延迟消息工作正常;当延迟时间为4294967296毫秒的时候,消息被直接消费,没有延迟效果。
所以,我们在使用RabbitMQ的延迟消息功能时候,必须注意它的延迟极限是4294967295毫秒(49.7天)。如果你的业务需求会超过这个临界值,就必须避开这个坑,采用其他方法来实现需要延迟或者定时执行的任务了。
