自定义与 IBM MQ 的自动重新连接设置

Customize the automatic reconnection settings to IBM MQ

我已经编写了连接到 IBM MQ 的代码,我正在使用自动重新连接到 ibm mq 的 ConnectionNameList。

我想自定义隐式发生的重新连接。我在互联网上参考了很多文章,但我无法弄明白。

这是我的队列管理器配置:

@Configuration
public class QM1Config{
    

    public String queueManager;
    public String queue;
    public String channel;
    public String connName;
    public String user;
    public String password;
    private static final int RECONNECT_TIMEOUT = 10;
    
    @Autowired
    MQService config;
    

@Bean
public MQConnectionFactory mqQueueConnectionFactory() {
    this.channel = config.getHosts().get(0).getChannel();
    this.user = config.getHosts().get(0).getUser();
    this.password = config.getHosts().get(0).getPassword();
    this.queueManager = config.getHosts().get(0).getQueueManager();
    this.queue = config.getHosts().get(0).getQueue();
    this.connName = config.getHosts().get(0).getConnName();
    System.out.println(channel+" "+connName+" "+queueManager+" "+user);
    MQConnectionFactory mqQueueConnectionFactory = new MQConnectionFactory();
    try {
        mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
        mqQueueConnectionFactory.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, false);
        mqQueueConnectionFactory.setCCSID(1208);
        mqQueueConnectionFactory.setChannel(channel);
        mqQueueConnectionFactory.setStringProperty(WMQConstants.USERID, user);
        mqQueueConnectionFactory.setStringProperty(WMQConstants.PASSWORD, password);
        mqQueueConnectionFactory.setQueueManager(queueManager);
        mqQueueConnectionFactory.setConnectionNameList(connName);
    } catch (Exception e) {
        e.printStackTrace();
        
        
    }
    return mqQueueConnectionFactory;
 
}


@Bean
public JmsListenerContainerFactory<?> qm1JmsListenerContainerFactory(@Qualifier("mqQueueConnectionFactory") MQConnectionFactory mqQueueConnectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer) throws InterruptedException {
  DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
  this.queue = config.getHosts().get(0).getQueue();
  configurer.configure(factory, mqQueueConnectionFactory);
  return factory;
}

@Bean("jmsTemplate1")
public JmsTemplate jmsTemplate(@Qualifier("mqQueueConnectionFactory") MQConnectionFactory mqQueueConnectionFactory) {
    JmsTemplate jmsTemplate1  = new JmsTemplate(mqQueueConnectionFactory);
    return jmsTemplate1;
}


}

当我停止队列管理器时,我每 5 秒收到以下异常:

2022-04-24 01:17:43.194  WARN 6644 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer  : Setup of JMS message listener invoker failed for destination 'Q5' - trying to recover. Cause: JMSWMQ2002: Failed to get a message from destination 'Q5'.; nested exception is com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2009' ('MQRC_CONNECTION_BROKEN').
2022-04-24 01:17:43.232 ERROR 6644 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer  : Could not refresh JMS Connection for destination 'Q5' - retrying using FixedBackOff{interval=5000, currentAttempts=0, maxAttempts=unlimited}. Cause: JMSWMQ0018: Failed to connect to queue manager 'QM5' with connection mode 'Client' and host name 'Client'.; nested exception is com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2059' ('MQRC_Q_MGR_NOT_AVAILABLE').
2022-04-24 01:17:48.243 ERROR 6644 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer  : Could not refresh JMS Connection for destination 'Q5' - retrying using FixedBackOff{interval=5000, currentAttempts=1, maxAttempts=unlimited}. Cause: JMSWMQ0018: Failed to connect to queue manager 'QM5' with connection mode 'Client' and host name 'Client'.; nested exception is com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2538' ('MQRC_HOST_NOT_AVAILABLE').
2022-04-24 01:17:53.245 ERROR 6644 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer  : Could not refresh JMS Connection for destination 'Q5' - retrying using FixedBackOff{interval=5000, currentAttempts=2, maxAttempts=unlimited}. Cause: JMSWMQ0018: Failed to connect to queue manager 'QM5' with connection mode 'Client' and host name 'Client'.; nested exception is com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2538' ('MQRC_HOST_NOT_AVAILABLE').
2022-04-24 01:17:58.250 ERROR 6644 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer  : Could not refresh JMS Connection for destination 'Q5' - retrying using FixedBackOff{interval=5000, currentAttempts=3, maxAttempts=unlimited}. Cause: JMSWMQ0018: Failed to connect to queue manager 'QM5' with connection mode 'Client' and host name 'Client'.; nested exception is com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2538' ('MQRC_HOST_NOT_AVAILABLE').

所以我希望前 3 次重新连接尝试应该是一条警告消息,而不是上面日志中所示的错误消息,而从第 4 次尝试开始,我希望它是一条错误消息。并且每 10/15 秒尝试重新连接一次。

如何配置这些重新连接设置?

如有任何帮助,我们将不胜感激!谢谢!

编辑:我添加了一个异常侦听器,如下所示:

public class MQExceptionListener implements ExceptionListener {
    private static final Logger LOGGER = LoggerFactory.getLogger(MQExceptionListener.class);

    int count = -1;
    

    @Override
    public void onException(JMSException ex) {
        
        if(count > 2) {
            System.out.println("COUNT - "+ count);
            count++;
                     LOGGER.error("***********************************************");
                        LOGGER.error(ex.toString()+" THIS IS EX TO STRING");
                        if (ex.getLinkedException() != null) {
                            LOGGER.error(ex.getLinkedException().toString()+" THIS IS getLinkedException TO STRING");
                        }
                        LOGGER.error("================================================");

        }else {
            System.out.println("COUNT - "+ count);
            count++;
                     LOGGER.warn("***********************************************");
                        LOGGER.warn(ex.toString()+" THIS IS EX TO STRING");
                        if (ex.getLinkedException() != null) {
                            LOGGER.warn(ex.getLinkedException().toString()+" THIS IS getLinkedException TO STRING");
                        }
                        LOGGER.warn("================================================");
                    
        }
            
    }
}

现在我的日志如下:

COUNT - 1
2022-04-24 14:41:04.905  WARN 9268 --- [enerContainer-1] com.mq.sslMQ.MQExceptionListener         : ***********************************************
2022-04-24 14:41:04.905  WARN 9268 --- [enerContainer-1] com.mq.sslMQ.MQExceptionListener         : com.ibm.msg.client.jms.DetailedIllegalStateException: JMSWMQ0018: Failed to connect to queue manager 'QM5' with connection mode 'Client' and host name 'Client'.
Check the queue manager is started and if running in client mode, check there is a listener running. Please see the linked exception for more information. THIS IS EX TO STRING
2022-04-24 14:41:04.905  WARN 9268 --- [enerContainer-1] com.mq.sslMQ.MQExceptionListener         : com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2538' ('MQRC_HOST_NOT_AVAILABLE'). THIS IS getLinkedException TO STRING
2022-04-24 14:41:04.905  WARN 9268 --- [enerContainer-1] com.mq.sslMQ.MQExceptionListener         : ================================================
2022-04-24 14:41:04.905 ERROR 9268 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer  : Could not refresh JMS Connection for destination 'Q5' - retrying using FixedBackOff{interval=5000, currentAttempts=1, maxAttempts=unlimited}. Cause: JMSWMQ0018: Failed to connect to queue manager 'QM5' with connection mode 'Client' and host name 'Client'.; nested exception is com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2538' ('MQRC_HOST_NOT_AVAILABLE').

我不想将默认消息侦听器容器日志打印到控制台。我们如何实现这一目标?

它在 IBM Docs 中说:-

By default, the reconnection attempts happen at the following intervals:

  • The first attempt is made after an initial delay of 1 second, plus a random element up to 250 milliseconds.
  • The second attempt is made 2 seconds, plus a random interval of up to 500 milliseconds, after the first attempt fails.
  • The third attempt is made 4 seconds, plus a random interval of up to 1 second, after the second attempt fails.
  • The fourth attempt is made 8 seconds, plus a random interval of up to 2 seconds, after the third attempt fails.
  • The fifth attempt is made 16 seconds, plus a random interval of up to 4 seconds, after the fourth attempt fails.
  • The sixth attempt, and all subsequent attempts are made 25 seconds, plus a random interval of up to 6 seconds and 250 milliseconds after the previous attempt fails.

The reconnection attempts are delayed by intervals that are partly fixed and partly random. This is to prevent all of the IBM MQ classes for JMS applications that were connected to a queue manager that is no longer available from reconnecting simultaneously.

If you need to increase the default values, to more accurately reflect the amount of time that is required for a queue manager to recover, or a standby queue manager to become active, modify the ReconDelay attribute in the Channel stanza of the client configuration file.

您可以阅读有关此属性的更多信息 here

听起来您需要将以下内容放入您的 mqclient.ini 文件中。

CHANNELS:
  ReconDelay=(10000,5000)

也就是要求延迟10秒加上一个随机元素最多5秒,这是我对你要求10/15秒的解读。您没有要求任何重新连接尝试在时间上有所不同,但如果需要,您可以这样做。

请注意,无法更改消息的 WARN/ERROR 状态。

请记住,您始终可以关闭自动重新连接并通过捕获应用程序中的连接失败来实现您需要的任何内容。自动重新连接专为无法(或不愿意)捕获连接失败的应用程序而设计。

我要补充的是,5 秒间隔的重新连接尝试是 DefaultMessageListenerContainer 尝试重新连接。默认重连间隔是5秒-DEFAULT_RECOVERY_INTERVAL,所以我认为这不涉及MQ重连机制。

有了上面列出的异常处理程序,您可以通过编程方式更改 DefaultMessageListenerContainer setRecoveryInterval() 或使用 setBackOff() 来控制退避间隔。

至于禁用日志记录,将 DefaultMessageListenerContainer 的日志级别设置为 FATAL 即可。