rabbitmq学习笔记 ----- 多级消息延迟始终为 20s 问题排查
问题现象
在实现多级延迟消息功能时,发现每次消息延迟间隔始终为20s,无法按照预期依次使用20s→10s→5s的延迟时间。日志显示每次处理时移除的延迟时间都是20000L。
问题代码片段
1.生产者
@Testvoid sendDelayMessage2() {List<Long> expireTimeList = new ArrayList<>();{expireTimeList.add(20000L);expireTimeList.add(10000L);expireTimeList.add(5000L);}delayMessage<String> msg = new delayMessage<>("hello,world222!", expireTimeList);rabbitTemplate.convertAndSend("delay.exchange", "hi",msg, message -> {message.getMessageProperties().setDelay(msg.removeDelayTime().intValue());return message;});}
2.消费者
@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "delay.queue", durable = "true"),exchange = @Exchange(value = "delay.exchange", delayed = "true"),key = "hi"))public void listenMultiDelay(delayMessage<String> msg) {log.info("收到消息: {}" , msg.getMessage());if(msg.hasExpireTime()){log.info("消息还有延迟时间,继续投递,剩余延迟时间:" );rabbitTemplate.convertAndSend("delay.exchange", "hi",msg, message -> {message.getMessageProperties().setDelay(msg.removeDelayTime().intValue());return message;});}}
3.延迟消息类
@Data
public class delayMessage<T> {private T message;private List<Long> expireTime;public delayMessage() {}public delayMessage(T message, List<Long> expireTime) {this.message = message;this.expireTime = expireTime;}public Long removeDelayTime(){System.out.println("removeDelayTime: "+expireTime.get(0));return expireTime.remove(0);}public boolean hasExpireTime(){return !expireTime.isEmpty();}}
4.启动配置
@Beanpublic MessageConverter messageConverter() {return new Jackson2JsonMessageConverter();}
核心原因分析
问题根源Jackson2JsonMessageConverter的序列化特性与业务逻辑设计之间的冲突有关:
-
Jackson2JsonMessageConverter工作机制
- 发送消息时,将对象序列化为JSON字符串(数据快照)
- 接收消息时,将JSON字符串重新反序列化为全新的对象实例,而非复用原对象
-
对delayMessage对象的影响
- 发送端调用
removeDelayTime()
后,内存中对象的expireTime
列表已变为[10000L, 5000L]
- 但序列化保存的是操作前的状态快照(包含20000L的完整列表)
- 消费者接收时,反序列化创建的是新对象,
expireTime
列表恢复为初始状态[20000L, 10000L, 5000L]
- 导致每次处理都从20000L开始,形成无限循环
- 发送端调用
-
总结:说人话就是 Jackson2JsonMessageConverter 将消息转换成json格式前,delayMessage中的expireTime还没有收到
removeDelayTime()
的影响而改变
解决方案: 先改对象,再序列化
修正后代码:生产者(消费者同理)
@Testvoid sendDelayMessage2() {List<Long> expireTimeList = new ArrayList<>();{expireTimeList.add(20000L);expireTimeList.add(10000L);expireTimeList.add(5000L);}delayMessage<String> msg = new delayMessage<>("hello,world222!",expireTimeList);// 先进行延迟时间的更新Long delaytime = msg.removeDelayTime();rabbitTemplate.convertAndSend("delay.exchange", "hi",msg, message -> {message.getMessageProperties().setDelay(delaytime.intValue());return message;});}
通过以上调整,多级延迟消息可按预期依次使用20s→10s→5s的延迟时间,最终解决了始终20s延迟的问题。