ActiveMQ Artemis 2.17主从HA下“投递中”消息卡住不释放问题问询
问题梳理
你在2.17版本ActiveMQ Artemis双节点主从(共享文件系统HA)架构下遇到的投递消息永久滞留问题,核心复现特征如下:
- 强制关停主节点后从节点可正常接管服务,消费端能收到后续新流入的消息
- 队列中部分消息永久卡在
in delivery状态,即使存在在线消费者、新消息流转正常,这类消息也不会被重投 - 已尝试配置Broker端
<connection-ttl-override>、客户端连接串设置connectionTtl、切换CachingConnectionFactory/JmsPoolConnectionFactory两种连接池实现,均无法触发残留连接清理、释放滞留消息 - 仅能通过重启消费者应用解锁滞留消息,无法满足生产环境可用性要求
根因定位
你最初判断的“残留连接绑定投递状态导致消息无法回收”的方向是正确的,之前配置不生效核心是踩了两个版本和配置层面的坑:
- 2.17版本HA场景的持久化状态清理缺陷
主节点被强制关停时,未完成事务确认的投递状态会被持久化到共享存储;从节点接管加载队列元数据时,会直接把这部分消息标记为in delivery状态,绑定到原来的会话ID上。但这些会话对应的TCP连接已经随主节点关停消失,从节点根本没有对应的连接上下文,常规的connection-ttl/connection-ttl-override配置因为找不到对应的连接对象,完全不会触发超时清理逻辑,消息就永久卡在当前状态。 - 客户端连接缓存的失效残留
你使用的CachingConnectionFactory默认会缓存Session、Consumer实例,当主从切换底层TCP连接断开重连后,缓存里的旧Session句柄没有被正确校验有效性,导致Broker侧给旧Session标记的投递消息,永远等不到对应的事务提交确认信号,进一步加重滞留问题。你之前仅设置cacheConsumers=false没有解决问题,是因为Session缓存依然存在,对transacted模式的消费者来说,失效的Session会直接导致投递消息无响应。
无需重启消费者的自动释放方案
方案1:Broker队列层配置投递超时(最适配场景,优先推荐)
这个是Artemis原生提供的能力,直接在队列层面监听消息的投递时长,不依赖TCP连接上下文,完全绕开之前TTL配置不生效的问题。
在broker.xml中针对业务队列增加投递超时配置,超时后自动将in delivery状态的消息重置、重新入队参与正常投递:
<address name="myQueue"> <anycast> <queue name="myQueue"> <!-- 投递中消息的超时时间,单位毫秒,建议设置为业务最大处理时长的1.5倍 --> <default-delivery-timeout>30000</default-delivery-timeout> <!-- 超时时间的动态调整系数,默认1.0即可 --> <delivery-timeout-scaler>1.0</delivery-timeout-scaler> </queue> </anycast> </address>
配置生效后,哪怕是HA切换产生的僵尸会话绑定的滞留消息,只要超过30秒未被确认,Broker就会自动回收消息状态,不需要等待连接失效判定,完全实现自动解锁,无人工干预要求。
方案2:修正客户端连接配置,从源头减少僵尸会话产生
针对你基于Spring JMS的消费端场景,调整连接工厂配置,消除缓存导致的会话失效问题:
- 替换全量缓存的
CachingConnectionFactory,使用Artemis生态适配的JmsPoolConnectionFactory,对事务型消费者关闭Session和Consumer缓存:
<bean id="pooledConnectionFactory" class="org.messaginghub.pooled.jms.JmsPoolConnectionFactory"> <property name="connectionFactory"> <bean class="org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory"> <property name="brokerURL" value="${spring.artemis.brokerUrl}" /> <property name="user" value="${spring.artemis.user}" /> <property name="password" value="${spring.artemis.password}" /> <!-- 客户端每10秒检测一次连接有效性,每30秒无流量的连接主动判定失效 --> <property name="clientFailureCheckPeriod" value="10000" /> <property name="connectionTTL" value="30000" /> </bean> </property> <!-- 事务型消费者关闭缓存,避免重连后使用失效的旧Session句柄 --> <property name="cacheSession" value="false" /> <property name="cacheConsumers" value="false" /> <property name="maxConnections" value="10" /> </bean>
- 适配现有的
int-jms:message-driven-channel-adapter配置,保持acknowledge="transacted"的事务模式不变,即可自动适配新的连接工厂,HA切换后不会残留无效的Session引用。
方案3:运维侧兜底自动化巡检
如果暂不准备调整现有Broker和客户端配置,可以通过Artemis原生的JMX管理接口做定时兜底:
- 每30秒巡检一次目标队列的消息状态,筛选出投递时长超过业务阈值、状态为
in delivery的滞留消息,调用队列的消息恢复接口,强制重置消息状态并重新入队 - 该逻辑可以集成到现有运维监控平台,不需要改动业务代码,完全自动运行,作为方案1的补充兜底。
内容的提问来源于stack exchange,提问作者ypanag
相关产品推荐
相关产品推荐

