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

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中已经正确配置了事务型会话,但再确认几个关键点:

  1. setSessionTransacted(true)和setSessionAcknowledgeMode(SESSION_TRANSACTED)已经生效,这会让会话处于事务模式,只有commit后消息才会被移除。
  2. setMaxMessagesPerTask(1)确保每次只处理一条消息,避免批量处理导致的事务问题。
  3. ErrorHandler只是记录日志,没有吞掉异常,这部分你的配置是正确的。

四、验证效果

完成上述修改后,发送一条消息到目标队列:

  1. 你会看到控制台打印5次(对应max-delivery-attempts=5)消息处理失败的日志。
  2. 重试次数耗尽后,消息会被转移到DLQ队列中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:02:21