Spring Boot与ActiveMQ Classic基于OpenWire JMS客户端的重试问题排查
问题分析与解决方案
问题原因
- ActiveMQ默认重试机制冲突:ActiveMQ Classic默认
maxRedeliveries值为6,加上首次消费请求,总共会触发7次无间隔重试,这是你看到当前现象的直接原因。 - Spring Retry未生效:你在
@JmsListener方法上同时添加了@Transactional,当方法抛出异常时,JMS事务会直接回滚,消息被ActiveMQ Broker重新投递,此时Spring Retry的逻辑根本不会触发——消息是被Broker重新发送给监听器,而非在同一个方法调用内执行重试。
解决方案
根据你60秒延迟、最多4次重试的需求,提供两种可行方案:
方案一:使用Spring Retry处理重试(需关闭ActiveMQ自动重试)
1. 关闭Broker端自动重投递
修改ConnectionFactory配置,禁用ActiveMQ自带的重试机制:
@Configuration @Slf4j public class JmsConfig { @Value("${jms.activemq.brokerUrl}") private String brokerUrl; @Value("${jms.activemq.user}") private String brokerUsername; @Value("${jms.activemq.password}") private String brokerPassword; @Bean public ConnectionFactory connectionFactory() { ActiveMQSslConnectionFactory factory = new ActiveMQSslConnectionFactory(); factory.setBrokerURL(brokerUrl); factory.setUserName(brokerUsername); factory.setPassword(brokerPassword); factory.setTrustAllPackages(true); // 配置重投递策略,关闭Broker自动重试 RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy(); redeliveryPolicy.setMaximumRedeliveries(0); factory.setRedeliveryPolicy(redeliveryPolicy); return factory; } // 其他Bean配置不变 }
2. 调整重试与事务的作用范围
将业务逻辑抽离为单独方法,让@Retryable作用于业务方法,@JmsListener仅负责接收消息:
@JmsListener(destination = "test") public void receiveMessage(ObjectMessage message) throws JMSException { LOG.info("receiveMessage started......"); try { processMessage(message); } catch (Exception e) { LOG.error("最终处理失败,消息将被确认", e); message.acknowledge(); } } @Retryable(maxAttempts = 4, backoff = @Backoff(delay = 60000), include = {JMSException.class, ClassCastException.class}) @Transactional public void processMessage(ObjectMessage message) throws JMSException { // 原业务逻辑代码 ....... } @Recover public void recover(Exception e, ObjectMessage message) throws JMSException { LOG.error("4次重试后仍处理失败", e); // 可在此处添加死信队列投递等降级逻辑 }
注意:需确保项目已引入
spring-boot-starter-aop和spring-retry依赖。
方案二:直接使用ActiveMQ延迟重投递(无需Spring Retry)
如果不想依赖Spring Retry,可直接配置ActiveMQ的重投递策略实现需求:
@Bean public ConnectionFactory connectionFactory() { ActiveMQSslConnectionFactory factory = new ActiveMQSslConnectionFactory(); factory.setBrokerURL(brokerUrl); factory.setUserName(brokerUsername); factory.setPassword(brokerPassword); factory.setTrustAllPackages(true); RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy(); redeliveryPolicy.setMaximumRedeliveries(3); // 重试3次,加上首次共4次 redeliveryPolicy.setInitialRedeliveryDelay(60000); // 首次延迟60秒 redeliveryPolicy.setRedeliveryDelay(60000); // 每次重试间隔固定60秒 redeliveryPolicy.setUseExponentialBackOff(false); // 关闭指数退避 factory.setRedeliveryPolicy(redeliveryPolicy); return factory; }
然后移除@Retryable注解,由ActiveMQ Broker负责重试逻辑:
@JmsListener(destination = "test") @Transactional public void receiveMessage(ObjectMessage message) throws JMSException { LOG.info("receiveMessage started......"); try { ....... } catch (JMSException | ClassCastException e) { LOG.error("Error while processing the message", e); throw e; } }
重试失败后,消息会自动进入ActiveMQ死信队列(需确保Broker已配置死信队列)。
关键注意事项
- 禁止同时启用Spring Retry和ActiveMQ Broker重试,否则会导致重复重试逻辑,行为不可控。
- 建议配置死信队列,避免重试失败的消息丢失,可通过ActiveMQ配置文件或连接工厂的
deadLetterStrategy设置。
内容的提问来源于stack exchange,提问作者pppp
相关产品推荐
相关产品推荐

