《一次静态 ObjectMapper 引发的 RocketMQ 消费异常排查》
静态 ObjectMapper 引发的 RocketMQ 消费异常
- 一、问题背景
- 二、问题原因
- 三、思考与采取措施
- 四、根本原因(Spring 注入 & 静态变量 & RocketMQ 消费线程)
- 五、解决方案
- 六、总结
一、问题背景
在工作中日常使用 RocketMQ 作为消息队列进行异步处理时,发现一个很奇怪的现象:
- 消费者注册成功,控制台无报错 ✅
- 消息发送正常✅
- 但是就是收不到消息 ,没有任何异常的日志❌
二、问题原因
// 问题代码
private static final ObjectMapper objectMapper = new ObjectMapper();@Overridepublic void onMessage(String msg) {try {MyDTO dto = objectMapper.readValue(msg, MyDTO.class);log.info("Received and parsed message: {}", dto);// TODO: process dto} catch (Exception e) {log.error("Failed to deserialize message: {}", msg, e);}}
现象描述:
- 消费方法 onMessage 从未被触发
- RocketMQ 控制台看消息已经成功发送
- 本地没日志也没报错
三、思考与采取措施
排查顺序:
- 检查 topic 和 group 是否一致
- 检查是否开启消费(listener 生效)
- 查看消息轨迹,确认消息已投递
- 添加日志发现 onMessage 根本没有触发
- 怀疑是反序列化失败,但是catch 里面也没有打日志出来
- objectMapper 换成 @Autowired,结果立刻恢复正常
四、根本原因(Spring 注入 & 静态变量 & RocketMQ 消费线程)
- static final ObjectMapper 手动 new 出来的,不受 Spring 管理
- Spring 注入的 @Autowired ObjectMapper 是配置完整、统一管理的
- 如果反序列化失败,RocketMQ 默认不会抛出可见异常,尤其在异步线程中可能被吞掉
- static 变量初始化早于 Spring 生命周期,可能没准备好就被调用
五、解决方案
- 不要手动 new ObjectMapper,使用 Spring 注入
@Autowired
private ObjectMapper objectMapper;
六、总结
这个问题看似“像是线程问题”,实则是Spring 生命周期 + 组件注入机制的组合坑。
教训:当 Spring 项目中用到第三方组件(如 ObjectMapper)时,优先用 Spring 提供的注入,而非自己手动 new!