Spring Boot @JmsListener设CLIENT_ACKNOWLEDGE未调用ack仍消费消息
问题:Spring Boot中@JmsListener配置CLIENT_ACKNOWLEDGE但未调用acknowledge()消息仍被消费
在Spring Boot项目中使用@JmsListener消费消息,已将Session设置为CLIENT_ACKNOWLEDGE,同时在application.properties配置了spring.jms.listener.acknowledge-mode=CLIENT,但未调用msg.acknowledge()方法时,消息依然被消费掉。
相关配置代码
MQ配置类
import javax.jms.ConnectionFactory; import javax.jms.Session; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jms.annotation.EnableJms; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; @EnableJms @Configuration public class MQConfiguration { protected final Log logger = LogFactory.getLog(getClass()); @Bean(name = "jmsListenerContainerFactory") DefaultJmsListenerContainerFactory jmsContainerFactory(ConnectionFactory connectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setErrorHandler(t -> { logger.warn("An error has occurred in the transaction"); logger.error(t.getCause().getMessage()); }); factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); configurer.configure(factory, connectionFactory); factory.setConcurrency("1"); factory.setSessionTransacted(false); return factory; } }
消息消费方法
@JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "school.XXXX.request.clock-in", concurrency = "1") public void consumeMessageFromMQ(String message, Message msg) throws Throwable { // 业务逻辑,未调用msg.acknowledge() }
依赖版本信息
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.15</version> <relativePath /> </parent> <properties> <java.version>17</java.version> </properties> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>artemis-jms-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-artemis</artifactId> </dependency>
原因分析及解决办法
核心原因
- 配置顺序错误:你先设置了
Session.CLIENT_ACKNOWLEDGE,再调用configurer.configure(factory, connectionFactory),而DefaultJmsListenerContainerFactoryConfigurer会根据Spring Boot的自动配置参数覆盖你手动设置的会话确认模式。 - Spring JMS容器默认行为:即便是配置了
CLIENT_ACKNOWLEDGE,DefaultJmsListenerContainerFactory默认会在消息消费方法正常执行完成后自动确认消息,无需手动调用acknowledge()。
解决办法
1. 调整配置顺序,确保自定义配置生效
把configurer.configure的调用放在自定义配置之前,让手动设置覆盖自动配置:
@Bean(name = "jmsListenerContainerFactory") DefaultJmsListenerContainerFactory jmsContainerFactory(ConnectionFactory connectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); // 先应用自动配置 configurer.configure(factory, connectionFactory); factory.setErrorHandler(t -> { logger.warn("An error has occurred in the transaction"); logger.error(t.getCause().getMessage()); }); // 再设置自定义参数,覆盖自动配置 factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); factory.setConcurrency("1"); factory.setSessionTransacted(false); return factory; }
2. 禁用容器自动确认,完全手动控制
如果需要必须手动调用acknowledge()才确认消息,需添加setAcknowledgeOnException(false)配置:
factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); factory.setSessionTransacted(false); // 禁用异常时的自动确认,且正常执行后也不自动确认 factory.setAcknowledgeOnException(false);
配置后,只有手动调用msg.acknowledge()时消息才会被确认;方法抛出异常时,消息不会自动确认,会退回队列。
3. 检查Artemis Broker配置
确认Artemis Broker的consumer-window-size等参数没有设置为自动确认消息的模式,避免Broker主动确认未被客户端处理的消息。
内容的提问来源于stack exchange,提问作者Mowrya K B P
相关产品推荐
相关产品推荐

