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

微服务多实例下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')

问题原因

  1. 订阅元数据冲突:修改订阅名或Client ID后,旧的持久订阅未被正确清理,MQ内部仍标记其为活跃状态,导致新的订阅请求无法创建或加入共享组。
  2. Client ID重复:共享持久订阅要求每个实例使用唯一的Client ID,若多个实例Client ID重复,会触发订阅占用错误。
  3. MQ元数据显示问题:MQ Explorer可能因权限不足或元数据同步延迟,无法显示已存在的订阅,但实际订阅记录仍保存在MQ系统队列中。
  4. 事务未正常提交:开启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队列管理器,清除内存中的残留元数据。

验证操作

  1. 启动单个服务实例,确认能正常创建共享持久订阅,且可在MQ Explorer中查看该订阅。
  2. 启动第二个实例,检查是否不再抛出MQRC_SUBSCRIPTION_IN_USE错误,且消息仅被其中一个实例接收。
  3. 停止其中一个实例,确认剩余实例能正常接管消息消费,实现故障转移。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 10:20:32