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

如何在Spring JMS中实现UNORDER_ACKNOWLEDGMENT并控制消息确认

问题描述

我的Spring应用里有多个监听器,部署时偶尔会看到消息:Rejecting received message because of the listener container having been stopped in the meantime.,同时死信队列(DLQ)不断增长。当前用的是默认的自动确认(auto ack)模式,我想改成客户端确认(client ack)来控制消息确认逻辑。

我已经配置了以下Bean:

@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(SQSConnectionFactory sqsConnectionFactory) {
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    factory.setConnectionFactory(sqsConnectionFactory);
    factory.setDestinationResolver(new DynamicDestinationResolver());
    factory.setErrorHandler(this::handleError);
    factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
    return factory;
}

@Bean
public JmsTemplate jmsTemplate(SQSConnectionFactory sqsConnectionFactory) {
    return new JmsTemplate(sqsConnectionFactory);
}

@Bean
public AmazonSQS buildAmazonSqs() {
    return AmazonSQSClientBuilder.standard().withRegion(region).build();
}

@Bean
public SQSConnectionFactory sqsConnectionFactory(AmazonSQS amazonSQS) {
    return new SQSConnectionFactory(
            new ProviderConfiguration().withNumberOfMessagesToPrefetch(1),
            amazonSQS);
}

但测试发现,就算在@JmsListener里不执行message.acknowledge();,消息还是会从队列中被移除,不会回到队列,通过Datadog追踪能看到消息已被后台删除。把Session.CLIENT_ACKNOWLEDGE换成SQSSession.UNORDERED_ACKNOWLEDGE也没效果。

我想使用UNORDERED_ACKNOWLEDGMENT的原因是,根据AWS文档,CLIENT_ACKNOWLEDGMENT会确认之前的所有消息。

请问是否必须用@SqsListener替代Spring的@JmsListener?如果不需要,怎么在不调用message.acknowledge()的情况下阻止Spring自动确认消息?


解决方案

不需要替换为@SqsListener

Spring JMS配合AWS SQS完全可以实现客户端手动确认,问题出在容器工厂的额外配置上,默认的DefaultJmsListenerContainerFactory在使用SQS时,有几个关键参数需要调整:

  1. 关闭自动确认的隐式行为
    默认情况下,DefaultMessageListenerContainer(DefaultJmsListenerContainerFactory创建的容器)会在监听器方法正常执行完成后自动触发确认,哪怕你设置了CLIENT_ACKNOWLEDGE。要禁用这个行为,需要开启事务模式,同时配合指定的确认模式:

    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(SQSConnectionFactory sqsConnectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(sqsConnectionFactory);
        factory.setDestinationResolver(new DynamicDestinationResolver());
        factory.setErrorHandler(this::handleError);
        // 开启事务模式,禁用容器自动确认
        factory.setSessionTransacted(true);
        // 使用无序确认模式,避免确认当前消息时连带确认之前的所有消息
        factory.setSessionAcknowledgeMode(SQSSession.UNORDERED_ACKNOWLEDGE);
        return factory;
    }
    

    核心是setSessionTransacted(true),它会让容器放弃自动确认逻辑,完全由你通过message.acknowledge()手动控制消息确认时机。

  2. 优化预取与并发配置
    保留withNumberOfMessagesToPrefetch(1)的设置,避免预取过多未确认消息导致堆积;同时建议限制容器并发数为1(业务允许的情况下),避免多线程场景下的确认混乱:

    factory.setConcurrency("1");
    
  3. 异常场景的消息处理
    开启事务模式后,监听器抛出异常时,容器会回滚事务,消息会回到SQS队列(根据队列的可见性超时设置重试),而非直接进入DLQ。你可以通过自定义ErrorHandler来控制异常时的行为,比如添加重试次数限制后再转入DLQ。

之前配置不生效的原因

  • 非事务模式下,DefaultJmsListenerContainerFactory会忽略CLIENT_ACKNOWLEDGE设置,在监听器方法执行完成后自动调用acknowledge(),这是Spring JMS的默认简化行为,和SQS的客户端确认逻辑冲突。
  • SQSSession.UNORDERED_ACKNOWLEDGE需要配合事务模式才能生效,否则容器的自动确认会覆盖自定义的确认策略。

内容的提问来源于stack exchange,提问作者Bob213

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 16:29:53