You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
    }
}

方案二:改为客户端手动确认模式,先确认消息再执行业务

如果希望更灵活的控制确认时机,可将接收端改为客户端手动确认模式:

  1. 首先配置自定义的监听容器工厂,指定确认模式:
@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;
    }
}
  1. 调整消费方法,指定使用自定义容器工厂,先确认消息再执行业务:
@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 02:45:04