微服务多实例下MQ共享订阅异常及MQRC_SUBSCRIPTION_IN_USE报错排查
IBM MQ共享持久订阅多实例启动报错MQRC_SUBSCRIPTION_IN_USE问题排查与解决
问题描述
配置Spring JMS实现IBM MQ共享持久订阅,目标是微服务多实例运行时仅一个实例接收Topic消息。已在DefaultJmsListenerContainerFactory中设置subscriptionShared=true和subscriptionDurable=true,但多实例启动时所有实例均抛出MQRC_SUBSCRIPTION_IN_USE(2429)错误。此前使用非共享非持久订阅正常,改为共享持久后首次运行无问题;修改订阅名或Client ID后触发报错,且MQ Explorer中无法看到该持久订阅,怀疑存在未清理的陈旧异常订阅状态。
相关配置代码
Listener容器工厂配置
@Bean @Qualifier("subscriberListenerContainerFactory") public DefaultJmsListenerContainerFactory subscriberListenerContainerFactory(@Qualifier("subscriberConnectionFactory") ConnectionFactory subscriberConnectionFactory) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(subscriberConnectionFactory); factory.setDestinationResolver(new MyDestinationTopicResolver("Topic-JNDI-Name", "Topic-Name")); factory.setSubscriptionShared(true); factory.setPubSubDomain(true); factory.setSessionTransacted(true); factory.setSubscriptionDurable(true); factory.setConcurrency("1-1"); return factory; }
JMS监听器配置
@JmsListener(subscription = "My-Test-Subscription", destination = "dummy", containerFactory = "subscriberListenerContainerFactory", selector = "message_sub_type = 'MY_EVENT'") public void onMessage(Message message) throws JAXBException { ... }
报错信息
Caused by: com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '1' ('MQCC_WARNING') reason '2429' ('MQRC_SUBSCRIPTION_IN_USE')
问题原因
- 订阅元数据冲突:修改订阅名或Client ID后,旧的持久订阅未被正确清理,MQ内部仍标记其为活跃状态,导致新的订阅请求无法创建或加入共享组。
- Client ID重复:共享持久订阅要求每个实例使用唯一的Client ID,若多个实例Client ID重复,会触发订阅占用错误。
- MQ元数据显示问题:MQ Explorer可能因权限不足或元数据同步延迟,无法显示已存在的订阅,但实际订阅记录仍保存在MQ系统队列中。
- 事务未正常提交:开启
sessionTransacted=true后,实例异常关闭会导致未提交的事务残留,MQ无法正确标记订阅为非活跃。
解决步骤
1. 手动清理陈旧订阅
使用runmqsc工具连接MQ队列管理器,执行以下命令:
- 查询目标订阅:
DISPLAY SUB(*) TOPICSTR('你的Topic完整名称') SUBNAME('My-Test-Subscription') - 删除查询到的残留订阅:
# 通过订阅ID删除 DELETE SUB(SUBID='查询返回的SUBID值') # 或通过订阅名删除(确保订阅名唯一) DELETE SUB(SUBNAME='My-Test-Subscription')
2. 配置唯一Client ID
确保每个服务实例使用唯一的Client ID,避免重复:
- Spring Boot环境可通过配置文件动态生成:
spring.jms.client-id=my-service-${random.uuid} - 代码中可通过动态生成逻辑设置Client ID,比如结合实例IP、端口或容器ID。
3. 统一订阅配置
所有服务实例的DefaultJmsListenerContainerFactory配置必须完全一致,确保subscriptionShared、subscriptionDurable等属性统一,避免部分实例配置不匹配导致元数据冲突。
4. 调整订阅超时参数
设置订阅自动过期时间,让MQ自动清理长时间无活跃消费者的订阅:
ALTER SUB(SUBNAME='My-Test-Subscription') SUBEXP(30)
(SUBEXP=30表示30分钟无活跃连接则自动删除订阅)
5. 重启队列管理器(可选)
若手动删除订阅后问题仍存在,重启MQ队列管理器,清除内存中的残留元数据。
验证操作
- 启动单个服务实例,确认能正常创建共享持久订阅,且可在MQ Explorer中查看该订阅。
- 启动第二个实例,检查是否不再抛出
MQRC_SUBSCRIPTION_IN_USE错误,且消息仅被其中一个实例接收。 - 停止其中一个实例,确认剩余实例能正常接管消息消费,实现故障转移。
内容的提问来源于stack exchange,提问作者Rajib Biswas
相关产品推荐
相关产品推荐

