問題現象
在實現多級延遲消息功能時,發現每次消息延遲間隔始終為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延遲的問題。