SpringBoot集成HornetQ中Session.rollback()不生效问题求助
解决HornetQ中调用rollback()后消息不重发的问题
我来帮你一步步排查这个问题,先明确核心矛盾:你配置了事务型JMS会话,期望处理失败时rollback()触发消息重发,但目前调用后没有任何反应。咱们从代码逻辑和HornetQ配置两个维度来分析解决。
一、核心问题:手动rollback的时机不对
你当前的代码中,在try块里主动调用session.rollback(),然后方法正常结束(没有抛出异常)。但Spring的DefaultJmsListenerContainer对事务的处理逻辑是:
- 如果方法正常返回,容器会自动调用
commit() - 如果方法抛出异常,容器才会自动调用
rollback()
这就导致了冲突:你手动rollback后,容器仍会尝试commit事务,这不仅会引发IllegalStateException(事务已回滚),还会让HornetQ误以为消息已经被正确处理,不会触发重试。
修复代码:移除手动rollback,通过抛出异常触发容器自动回滚
修改你的Receiver类,让处理失败时抛出异常,交给Spring容器处理事务回滚,这样HornetQ才能正确识别需要重试的消息:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; import javax.jms.JMSException; import javax.jms.MapMessage; import javax.jms.Session; @Component public class Receiver { public static final Logger LOG = LoggerFactory.getLogger(Receiver.class); @JmsListener(destination = "${destination.name}", containerFactory = "myJmsContainerFactory") public void onReceive(final MapMessage message, Session session) throws JMSException { try { System.out.println(">>>> " + message); // 模拟业务处理失败(替换成你的实际业务逻辑) throw new RuntimeException("业务处理失败,触发重试"); } catch (Exception ex) { LOG.error("处理消息失败,准备回滚事务", ex); // 不需要手动rollback,抛出异常让容器自动处理回滚 throw new JMSException("消息处理失败", ex); } } }
二、关键配置:确保HornetQ开启重试和死信队列
即使事务回滚逻辑正确,如果HornetQ本身没有配置重试参数,消息也不会重发。你需要在HornetQ的服务器配置文件(如hornetq-configuration.xml)中,针对目标队列添加重试和死信队列的配置:
<address-settings> <!-- match属性填写你的队列名称,支持通配符 --> <address-setting match="${destination.name}"> <!-- 最大重试次数,超过后进入死信队列 --> <max-delivery-attempts>5</max-delivery-attempts> <!-- 初始重试间隔(毫秒) --> <retry-interval>1000</retry-interval> <!-- 重试间隔乘数(每次重试间隔乘以这个值) --> <retry-interval-multiplier>1.5</retry-interval-multiplier> <!-- 最大重试间隔(毫秒),防止间隔无限增大 --> <max-retry-interval>10000</max-retry-interval> <!-- 死信队列的地址 --> <dead-letter-address>DLQ</dead-letter-address> <!-- 是否将过期消息也发送到死信队列 --> <expiry-address>DLQ</expiry-address> </address-setting> </address-settings>
同时,确保你的死信队列DLQ已经在HornetQ中创建:
<queues> <queue name="DLQ"> <entry name="queue/DLQ"/> </queue> </queues>
三、验证容器配置的正确性
你的JmsConfig中已经正确配置了事务型会话,但再确认几个关键点:
setSessionTransacted(true)和setSessionAcknowledgeMode(SESSION_TRANSACTED)已经生效,这会让会话处于事务模式,只有commit后消息才会被移除。setMaxMessagesPerTask(1)确保每次只处理一条消息,避免批量处理导致的事务问题。ErrorHandler只是记录日志,没有吞掉异常,这部分你的配置是正确的。
四、验证效果
完成上述修改后,发送一条消息到目标队列:
- 你会看到控制台打印5次(对应
max-delivery-attempts=5)消息处理失败的日志。 - 重试次数耗尽后,消息会被转移到
DLQ队列中。
内容的提问来源于stack exchange,提问作者makson
相关产品推荐
相关产品推荐

