Spring JMS消费异常时如何避免消息回滚队列重复接收
Spring JMS消息重复消费问题解决方案
根因分析
- 发送端的
jmsTemplate配置仅作用于发送消息的会话,和接收端的消费会话完全独立,你在发送端设置的客户端确认模式、关闭事务的配置对接收端没有任何影响,这是之前配置不生效的核心原因。 - 从补充参数可见接收端的会话确认模式为
1,对应Session.AUTO_ACKNOWLEDGE,Spring JMS的该模式下,只有消费方法正常执行完成、没有抛出未捕获异常时才会确认消息,只要方法抛出未捕获的异常,消息就会被回滚到队列触发重发。 - 当前代码中手动调用的
message.acknowledge()不生效,因为该方法仅在会话确认模式为Session.CLIENT_ACKNOWLEDGE时才有效,自动确认模式下调用该方法不会产生任何作用。 - 当前代码的异常捕获范围仅包含
JMSException和IOException,validateEventRequest抛出的业务异常(如RuntimeException等)未被捕获,会直接抛出给Spring JMS框架触发消息重发。
解决方案
提供两种可直接落地的方案,按需选择即可:
方案一:无需修改确认模式,捕获全部异常避免抛出给框架
仅需调整消费方法的异常捕获逻辑,将所有异常内部消化不对外抛出,Spring JMS就会判定消费成功,自动确认消息不会重发:
@Override @JmsListener(destination = "${mq.receive_queue}") public void receive(Message message) { try { String jsonEvent = ((TextMessage) message).getText(); Event event = objectMapper.readValue(jsonEvent, Event.class); System.out.println(event.toString()); // 业务逻辑单独包裹,所有异常内部处理 try { validateEventRequest(event); } catch (Exception e) { // 此处可添加异常日志、告警、异常数据落库等逻辑 e.printStackTrace(); } } catch (Exception e) { // 兜底捕获所有异常,避免抛出给框架 e.printStackTrace(); } }
方案二:改为客户端手动确认模式,先确认消息再执行业务
如果希望更灵活的控制确认时机,可将接收端改为客户端手动确认模式:
- 首先配置自定义的监听容器工厂,指定确认模式:
@Configuration public class JmsConfig { @Bean public JmsListenerContainerFactory<?> jmsListenerContainerFactory(ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); // 关闭会话事务 factory.setSessionTransacted(false); // 设置为客户端手动确认模式 factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); return factory; } }
- 调整消费方法,指定使用自定义容器工厂,先确认消息再执行业务:
@Override @JmsListener(destination = "${mq.receive_queue}", containerFactory = "jmsListenerContainerFactory") public void receive(Message message) { try { String jsonEvent = ((TextMessage) message).getText(); Event event = objectMapper.readValue(jsonEvent, Event.class); System.out.println(event.toString()); // 先确认消息,后续业务异常不会触发重发 message.acknowledge(); validateEventRequest(event); } catch (Exception e) { // 异常内部处理即可 e.printStackTrace(); } }
注意事项
两种方案都建议添加业务异常的监控、落库逻辑,避免业务执行失败但没有感知,导致业务数据丢失。
内容的提问来源于stack exchange,提问作者Faramarz Afzali
相关产品推荐
相关产品推荐

