在使用自动重新连接的IBM MQ客户端应用程序中检测重新连接

时间:2020-01-27 21:20:38

标签: ibm-mq

我将JMS的IBM MQ类(IBM MQ版本8.0.0.4)用于配置了自动重新连接的情况。根据文档,重新连接是隐式发生的。我想在重新连接时发出一个简单的日志语句。因此,我需要在某种情况下以某种方式得到通知。

在第Application Recovery页的IBM文档中,我偶然发现“检测故障转移”部分的内容:

支持重新连接:使用以下命令注册MQCBT_EVENT_HANDLER事件处理程序: 队列管理器。事件处理程序通过MQRC_RECONNECTING发布 当客户端开始尝试重新连接到服务器时,以及 成功重新连接后的MQRC_RECONNECTED。然后,您可以运行 重新建立可预测状态的例程,以便客户端 应用程序能够继续处理。

不幸的是,我没有找到Java / JMS的代码示例来演示如何以及在何处注册这种事件处理程序。我不知道我的情况是否支持。谁能为我提供正确的指导,甚至提供代码示例?非常感谢。

2020年2月5日以来的问题更新:

在收到2020年1月27日Sashi的最初答复后,添加了以下由我创建的代码示例。

public static void main(String[] args) {
    Connection connection = null;
    Session session = null;
    Object destination = null;
    MessageProducer producer = null;

    try {
        JmsFactoryFactory jmsFactoryFactory = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
        JmsConnectionFactory cf = jmsFactoryFactory.createConnectionFactory();

        cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, HOST);
        cf.setIntProperty(WMQConstants.WMQ_PORT, PORT);
        cf.setStringProperty(WMQConstants.WMQ_CHANNEL, CHANNEL);
        cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
        cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, QM_NAME);
        cf.setIntProperty(WMQConstants.WMQ_CLIENT_RECONNECT_OPTIONS, WMQConstants.WMQ_CLIENT_RECONNECT);
        cf.setIntProperty(WMQConstants.WMQ_CLIENT_RECONNECT_TIMEOUT, RECONNECT_TIMEOUT);

        connection = cf.createConnection();
        connection.setExceptionListener(new MQExceptionListener());
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        destination = session.createQueue(QUEUE);
        producer = session.createProducer((Destination)destination);
        connection.start();
    } catch (JMSException ex) {
        LOGGER.error(ex.toString());
    }
}

public class MQExceptionListener implements ExceptionListener {
    public void onException(JMSException e) {
        System.out.println(e);
        if(e.getLinkedException() != null)
            System.out.println(e.getLinkedException());
    }
}

这是我在日志中得到的:

ERROR [Main.main:57] (main) com.ibm.msg.client.jms.DetailedIllegalStateException: JMSWMQ0018: Failed to connect to queue manager '<hostname>' with connection mode 'Client' and host name '<hostname>(<port>)'.
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.
ERROR [Main.main:61] (main) Inner exceptions:
ERROR [Main.main:65] (main) com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2538' ('MQRC_HOST_NOT_AVAILABLE').
ERROR [Main.main:65] (main) com.ibm.mq.jmqi.JmqiException: CC=2;RC=2538;AMQ9204: Connection to host '<hostname>(<port>)' rejected. [1=com.ibm.mq.jmqi.JmqiException[CC=2;RC=2538;AMQ9204: Connection to host '<hostname>/<ip>:<port>' rejected. [1=java.net.ConnectException[Connection refused: connect],3=<hostname>/<ip>:<port>,4=TCP,5=Socket.connect]],3=<hostname>(<port>),5=RemoteTCPConnection.bindAndConnectSocket]
ERROR [Main.main:65] (main) com.ibm.mq.jmqi.JmqiException: CC=2;RC=2538;AMQ9204: Connection to host '<hostname>/<ip>:<port>' rejected. [1=java.net.ConnectException[Connection refused: connect],3=<hostname>/<ip>:<port>,4=TCP,5=Socket.connect]
ERROR [Main.main:65] (main) java.net.ConnectException: Connection refused: connect

2020年2月11日以来的问题更新:

我已根据2020年2月5日Sashi收到的反馈添加了这些补充内容。

我试图构建一个连接到IBM MQ实例的最小应用程序。这是代码:

Application.java

public class Application {
    private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) {
        new Application().run();
    }

    private void run() {
        MQWriter writer = new MQWriter();
        int i = 1;
        while (true) {
            String message = "Hello Testing " + i;
            LOGGER.info("Sending message {} to MQ server...", message);
            writer.write(message);
            i++;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

MQConnectionDetails.java

public class MQConnectionDetails {
    public static final String HOST = "XXX.XXX.XXX.XXX";
    public static final int PORT = 1414;
    public static final String QM_NAME = "QM1";
    public static final String CHANNEL = "DEV.APP.SVRCONN";
    public static final String QUEUE = "DEV.QUEUE.1";
    public static final int RECONNECT_TIMEOUT = 60; // 1 minute
}

MQWriter.java

public class MQWriter {

    private static final Logger LOGGER = LoggerFactory.getLogger(MQWriter.class);

    private Connection connection = null;
    private Session session = null;
    private Object destination = null;
    private MessageProducer producer = null;

    public MQWriter() {
        try {
            JmsFactoryFactory jff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
            JmsConnectionFactory jcf = jff.createConnectionFactory();
            jcf.setStringProperty(WMQConstants.WMQ_HOST_NAME, MQConnectionDetails.HOST);
            jcf.setIntProperty(WMQConstants.WMQ_PORT, MQConnectionDetails.PORT);
            jcf.setStringProperty(WMQConstants.WMQ_CHANNEL, MQConnectionDetails.CHANNEL);
            jcf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
            jcf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, MQConnectionDetails.QM_NAME);
            jcf.setIntProperty(WMQConstants.WMQ_CLIENT_RECONNECT_OPTIONS, WMQConstants.WMQ_CLIENT_RECONNECT);
            jcf.setIntProperty(WMQConstants.WMQ_CLIENT_RECONNECT_TIMEOUT, MQConnectionDetails.RECONNECT_TIMEOUT);

            LOGGER.info("Initializing connection to write queue {} on {}:{}...",
                    MQConnectionDetails.QUEUE,
                    MQConnectionDetails.HOST,
                    MQConnectionDetails.PORT);
            connection = jcf.createConnection();
            connection.setExceptionListener(new MQExceptionListener());
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            destination = session.createQueue(MQConnectionDetails.QUEUE);
            producer = session.createProducer((Destination)destination);
            connection.start();
        } catch (JMSException ex) {
            LOGGER.error("Error initializing connection to write queue", ex);
        }
    }

    public void write(String message) {
        try {
            TextMessage textMessage = session.createTextMessage(message);
            producer.send(textMessage);
        } catch (Exception ex) {
            LOGGER.error("Error sending message to write queue", ex);
        }
    }
}

MQExceptionListener.java

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

    public void onException(JMSException ex) {
        LOGGER.error("=====");
        LOGGER.error(ex.toString());
        if (ex.getLinkedException() != null) {
            LOGGER.error(ex.getLinkedException().toString());
        }
        LOGGER.error("=====");
    }
}

我运行的测试方案如下:

  1. 请确保IBM MQ在TCP端口1414上可用(在Amazon EC2上运行的IBM MQ Docker容器)。
  2. 运行上面的应用程序(Application.java),并确保将消息发送到队列。
  3. 通过将端口从1414更改为1415来更改Amazon EC2安全组上的防火墙配置,这将使客户端无法使用IBM MQ。

这是我观察到的:

  • 只有90秒钟不活动之后,客户端才开始引发异常。我不明白,因为我的RECONNECT_TIMEOUT设置为60秒,所以这里要关闭30秒。
  • MQExceptionListener仅被调用一次(第一次)。
  • 没有原因码2544(MQRC_RECONNECTING)仅存在2009(MQRC_CONNECTION_BROKEN)。为什么会这样?

以下是引发的异常的摘要:

控制台例外:

2020-02-11 09:50:16,155 INFO [Application.run:21] (main) Sending message Hello Testing 13 to MQ server...
2020-02-11 09:50:17,285 INFO [Application.run:21] (main) Sending message Hello Testing 14 to MQ server...
2020-02-11 09:50:18,413 INFO [Application.run:21] (main) Sending message Hello Testing 15 to MQ server...
2020-02-11 09:50:19,555 INFO [Application.run:21] (main) Sending message Hello Testing 16 to MQ server...
2020-02-11 09:51:45,966 ERROR [MQExceptionListener.onException:14] (JMSCCThreadPoolWorker-6) =====
2020-02-11 09:51:45,966 ERROR [MQExceptionListener.onException:15] (JMSCCThreadPoolWorker-6) com.ibm.msg.client.jms.DetailedJMSException: JMSWMQ1107: A problem with this connection has occurred.
An error has occurred with the IBM MQ JMS connection.
Use the linked exception to determine the cause of this error.
2020-02-11 09:51:45,966 ERROR [MQExceptionListener.onException:17] (JMSCCThreadPoolWorker-6) com.ibm.mq.MQException: MQ delivered an asynchronous event with completion code '2', and reason '2009'.
2020-02-11 09:51:45,966 ERROR [MQExceptionListener.onException:19] (JMSCCThreadPoolWorker-6) =====
2020-02-11 09:51:45,967 ERROR [MQWriter.write:52] (main) Error sending message to write queue
com.ibm.msg.client.jms.DetailedJMSException: JMSWMQ2007: Failed to send a message to destination 'DEV.QUEUE.1'.
JMS attempted to perform an MQPUT or MQPUT1; however IBM MQ reported an error.
Use the linked exception to determine the cause of this error.
    at com.ibm.msg.client.wmq.common.internal.Reason.reasonToException(Reason.java:595)
    at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:215)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.checkJmqiCallSuccess(WMQMessageProducer.java:1288)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.checkJmqiCallSuccess(WMQMessageProducer.java:1245)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.access$800(WMQMessageProducer.java:76)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer$SpiIdentifiedProducerShadow.sendInternal(WMQMessageProducer.java:906)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer$ProducerShadow.send(WMQMessageProducer.java:566)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.send(WMQMessageProducer.java:1428)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.sendMessage(JmsMessageProducerImpl.java:855)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.synchronousSendInternal(JmsMessageProducerImpl.java:2055)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.sendInternal(JmsMessageProducerImpl.java:1993)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.send(JmsMessageProducerImpl.java:1486)
    at com.ibm.mq.jms.MQMessageProducer.send(MQMessageProducer.java:293)
    at org.example.MQWriter.write(MQWriter.java:50)
    at org.example.Application.run(Application.java:22)
    at org.example.Application.main(Application.java:13)
Caused by: com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2009' ('MQRC_CONNECTION_BROKEN').
    at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:203)
    ... 14 more
Caused by: com.ibm.mq.jmqi.JmqiException: CC=2;RC=2009
    at com.ibm.mq.jmqi.remote.api.RemoteHconn$ReconnectionState.recordFailure(RemoteHconn.java:4931)
    at com.ibm.mq.jmqi.remote.api.RemoteHconn.setReconnectionFailureInner(RemoteHconn.java:2650)
    at com.ibm.mq.jmqi.remote.api.RemoteParentHconn.setReconnectionFailure(RemoteParentHconn.java:152)
    at com.ibm.mq.jmqi.remote.impl.RemoteReconnectThread.bestHconn(RemoteReconnectThread.java:265)
    at com.ibm.mq.jmqi.remote.impl.RemoteReconnectThread.run(RemoteReconnectThread.java:115)
    at com.ibm.msg.client.commonservices.workqueue.WorkQueueItem.runTask(WorkQueueItem.java:319)
    at com.ibm.msg.client.commonservices.workqueue.SimpleWorkQueueItem.runItem(SimpleWorkQueueItem.java:99)
    at com.ibm.msg.client.commonservices.workqueue.WorkQueueItem.run(WorkQueueItem.java:343)
    at com.ibm.msg.client.commonservices.workqueue.WorkQueueManager.runWorkQueueItem(WorkQueueManager.java:312)
    at com.ibm.msg.client.commonservices.j2se.workqueue.WorkQueueManagerImplementation$ThreadPoolWorker.run(WorkQueueManagerImplementation.java:1227)
2020-02-11 09:51:46,969 INFO [Application.run:21] (main) Sending message Hello Testing 17 to MQ server...
2020-02-11 09:51:46,972 ERROR [MQWriter.write:52] (main) Error sending message to write queue
com.ibm.msg.client.jms.DetailedJMSException: JMSWMQ2007: Failed to send a message to destination 'DEV.QUEUE.1'.
JMS attempted to perform an MQPUT or MQPUT1; however IBM MQ reported an error.
Use the linked exception to determine the cause of this error.
    at com.ibm.msg.client.wmq.common.internal.Reason.reasonToException(Reason.java:595)
    at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:215)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.checkJmqiCallSuccess(WMQMessageProducer.java:1288)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.checkJmqiCallSuccess(WMQMessageProducer.java:1245)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.access$800(WMQMessageProducer.java:76)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer$SpiIdentifiedProducerShadow.sendInternal(WMQMessageProducer.java:906)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer$ProducerShadow.send(WMQMessageProducer.java:566)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.send(WMQMessageProducer.java:1428)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.sendMessage(JmsMessageProducerImpl.java:855)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.synchronousSendInternal(JmsMessageProducerImpl.java:2055)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.sendInternal(JmsMessageProducerImpl.java:1993)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.send(JmsMessageProducerImpl.java:1486)
    at com.ibm.mq.jms.MQMessageProducer.send(MQMessageProducer.java:293)
    at org.example.MQWriter.write(MQWriter.java:50)
    at org.example.Application.run(Application.java:22)
    at org.example.Application.main(Application.java:13)
Caused by: com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2009' ('MQRC_CONNECTION_BROKEN').
    at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:203)
    ... 14 more
Caused by: com.ibm.mq.jmqi.JmqiException: CC=2;RC=2009
    at com.ibm.mq.jmqi.remote.api.RemoteHconn$ReconnectionState.recordFailure(RemoteHconn.java:4931)
    at com.ibm.mq.jmqi.remote.api.RemoteHconn.setReconnectionFailureInner(RemoteHconn.java:2650)
    at com.ibm.mq.jmqi.remote.api.RemoteParentHconn.setReconnectionFailure(RemoteParentHconn.java:152)
    at com.ibm.mq.jmqi.remote.impl.RemoteReconnectThread.bestHconn(RemoteReconnectThread.java:265)
    at com.ibm.mq.jmqi.remote.impl.RemoteReconnectThread.run(RemoteReconnectThread.java:115)
    at com.ibm.msg.client.commonservices.workqueue.WorkQueueItem.runTask(WorkQueueItem.java:319)
    at com.ibm.msg.client.commonservices.workqueue.SimpleWorkQueueItem.runItem(SimpleWorkQueueItem.java:99)
    at com.ibm.msg.client.commonservices.workqueue.WorkQueueItem.run(WorkQueueItem.java:343)
    at com.ibm.msg.client.commonservices.workqueue.WorkQueueManager.runWorkQueueItem(WorkQueueManager.java:312)
    at com.ibm.msg.client.commonservices.j2se.workqueue.WorkQueueManagerImplementation$ThreadPoolWorker.run(WorkQueueManagerImplementation.java:1227)

2020年2月12日以来的问题更新

根据乔什·麦克(JoshMc)从2020年2月11日开始的回答添加了此样本和发现。我对这个样本的评论:

  • 我现在正在使用MQ *类,并根据建议设置重新连接选项。
  • 虽然仍然没有发生重新连接

MQWriter2.java

public class MQWriter2 {

    private static final Logger LOGGER = LoggerFactory.getLogger(MQWriter2.class);

    private Connection connection = null;
    private Session session = null;
    private Queue destination = null;
    private MessageProducer producer = null;

    public MQWriter2() {
        try {
            MQConnectionFactory factory = new MQConnectionFactory();
            factory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
            factory.setConnectionNameList("XXX.XXX.XXX.XXX(1414)");
            factory.setQueueManager(MQConnectionDetails.QM_NAME);
            factory.setChannel(MQConnectionDetails.CHANNEL);
            factory.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT);
            factory.setClientReconnectTimeout(MQConnectionDetails.RECONNECT_TIMEOUT);

            LOGGER.info("Initializing connection to write queue {} on {}:{}...",
                    MQConnectionDetails.QUEUE,
                    MQConnectionDetails.HOST,
                    MQConnectionDetails.PORT);
            connection = factory.createConnection();
            connection.setExceptionListener(new MQExceptionListener());
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            destination = session.createQueue(MQConnectionDetails.QUEUE);
            producer = session.createProducer(destination);
            connection.start();
        } catch (JMSException ex) {
            LOGGER.error("Error initializing connection to write queue", ex);
        }
    }

    public void write(String message) {
        try {
            TextMessage textMessage = session.createTextMessage(message);
            producer.send(textMessage);
        } catch (Exception ex) {
            LOGGER.error("Error sending message to write queue", ex);
        }
    }
}

控制台输出

2020-02-12 08:39:11,628 INFO [MQWriter2.<init>:29] (main) Initializing connection to write queue DEV.QUEUE.1 on 54.161.121.207:1414...
2020-02-12 08:39:14,552 INFO [Application.run:19] (main) Sending message Hello Testing 1 to MQ server...
2020-02-12 08:39:15,710 INFO [Application.run:19] (main) Sending message Hello Testing 2 to MQ server...
2020-02-12 08:39:16,841 INFO [Application.run:19] (main) Sending message Hello Testing 3 to MQ server...
...
2020-02-12 08:39:41,973 INFO [Application.run:19] (main) Sending message Hello Testing 25 to MQ server...
2020-02-12 08:41:27,314 ERROR [MQExceptionListener.onException:14] (JMSCCThreadPoolWorker-10) =====
2020-02-12 08:41:27,314 ERROR [MQExceptionListener.onException:15] (JMSCCThreadPoolWorker-10) com.ibm.msg.client.jms.DetailedJMSException: JMSWMQ1107: A problem with this connection has occurred.
An error has occurred with the IBM MQ JMS connection.
Use the linked exception to determine the cause of this error.
2020-02-12 08:41:27,314 ERROR [MQWriter2.write:49] (main) Error sending message to write queue
com.ibm.msg.client.jms.DetailedJMSException: JMSWMQ2007: Failed to send a message to destination 'DEV.QUEUE.1'.
JMS attempted to perform an MQPUT or MQPUT1; however IBM MQ reported an error.
Use the linked exception to determine the cause of this error.
    at com.ibm.msg.client.wmq.common.internal.Reason.reasonToException(Reason.java:595)
    at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:215)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.checkJmqiCallSuccess(WMQMessageProducer.java:1288)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.checkJmqiCallSuccess(WMQMessageProducer.java:1245)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.access$800(WMQMessageProducer.java:76)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer$SpiIdentifiedProducerShadow.sendInternal(WMQMessageProducer.java:906)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer$ProducerShadow.send(WMQMessageProducer.java:566)
    at com.ibm.msg.client.wmq.internal.WMQMessageProducer.send(WMQMessageProducer.java:1428)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.sendMessage(JmsMessageProducerImpl.java:855)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.synchronousSendInternal(JmsMessageProducerImpl.java:2055)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.sendInternal(JmsMessageProducerImpl.java:1993)
    at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.send(JmsMessageProducerImpl.java:1486)
    at com.ibm.mq.jms.MQMessageProducer.send(MQMessageProducer.java:293)
    at org.example.MQWriter2.write(MQWriter2.java:47)
    at org.example.Application.run(Application.java:20)
    at org.example.Application.main(Application.java:11)
Caused by: com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2009' ('MQRC_CONNECTION_BROKEN').
    at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:203)
    ... 14 more
Caused by: com.ibm.mq.jmqi.JmqiException: CC=2;RC=2009
    at com.ibm.mq.jmqi.remote.api.RemoteHconn$ReconnectionState.recordFailure(RemoteHconn.java:4931)
    at com.ibm.mq.jmqi.remote.api.RemoteHconn.setReconnectionFailureInner(RemoteHconn.java:2650)
    at com.ibm.mq.jmqi.remote.api.RemoteParentHconn.setReconnectionFailure(RemoteParentHconn.java:152)
    at com.ibm.mq.jmqi.remote.impl.RemoteReconnectThread.bestHconn(RemoteReconnectThread.java:265)
    at com.ibm.mq.jmqi.remote.impl.RemoteReconnectThread.run(RemoteReconnectThread.java:115)
    at com.ibm.msg.client.commonservices.workqueue.WorkQueueItem.runTask(WorkQueueItem.java:319)
    at com.ibm.msg.client.commonservices.workqueue.SimpleWorkQueueItem.runItem(SimpleWorkQueueItem.java:99)
    at com.ibm.msg.client.commonservices.workqueue.WorkQueueItem.run(WorkQueueItem.java:343)
    at com.ibm.msg.client.commonservices.workqueue.WorkQueueManager.runWorkQueueItem(WorkQueueManager.java:312)
    at com.ibm.msg.client.commonservices.j2se.workqueue.WorkQueueManagerImplementation$ThreadPoolWorker.run(WorkQueueManagerImplementation.java:1227)

3 个答案:

答案 0 :(得分:5)

创建连接后,可以在连接对象上设置ExceptionListener。尝试重新连接时,将调用ExceptionListener的onException方法。这是一个示例:

    ExceptionListener exceptionListener = new ExceptionListener(){
        @Override
        public void onException(JMSException e) {
            System.out.println(e);
            if(e.getLinkedException() != null)
                System.out.println(e.getLinkedException());
        }
    };
    MQQueueConnection connection = (MQQueueConnection) cf.createQueueConnection();
    connection.setExceptionListener(exceptionListener);

答案 1 :(得分:2)

我遇到了与主题启动器相同的问题。在花了数小时筛选互联网上的可用信息、与同事交谈并试图使重新连接正常工作后,我放弃并决定通过模拟难以理解的重新连接功能来解决这个问题。我希望它能帮助其他在 IBM MQ 上苦苦挣扎的人。我编写的类基本上做了两件事:

  1. 以增加的尝试间隔反复尝试连接到 IBM MQ。
  2. 连接后,设置一个错误处理程序,当连接出现问题时由 IBM MQ 触发(使用相同的重新连接逻辑)。

首先,这是类本身:

package com.raiks.mqclient;

import javax.jms.Destination;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;

import com.ibm.msg.client.jms.JmsConnectionFactory;
import com.ibm.msg.client.wmq.WMQConstants;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.raiks.mqclient.IbmMqMessageListener;

/**
 * This class implements the reconnection logic for JMS brokers that don't support it
 * In particular, it does it for IBM MQ with its incomprehensible reconnection algorithm
 * It's expected that each connection manager receives a separate connection factory
 * and a message listener - it's not guaranteed for those to be thread safe
 */
public final class IbmMqJmsConnectionManager {
    private static final Logger LOGGER = LoggerFactory.getLogger(IbmMqJmsConnectionManager.class);
    private static final int INITIAL_RECONNECTION_DELAY_MS = 6000;
    private static final int MAX_RECONNECTION_DELAY_MS = 60000;
    private static final String QUEUE_PREIX = "queue:///";

    private final String connectorName;
    private final JmsConnectionFactory connectionFactory;
    private final String queueName;

    private final IbmMqMessageListener messageListener;

    private final int initialReconnectionDelayMs;
    private final int maxReconnectionDelayMs;

    public IbmMqJmsConnectionManager(
        String connectorName,
        JmsConnectionFactory connectionFactory,
        String queueName,
        IbmMqMessageListener messageListener,
        int initialReconnectionDelayMs,
        int maxReconnectionDelayMs
    ) {
        this.connectorName = connectorName;
        this.connectionFactory = connectionFactory;
        this.queueName = queueName;
        this.messageListener = messageListener;
        this.initialReconnectionDelayMs = initialReconnectionDelayMs;
        this.maxReconnectionDelayMs = maxReconnectionDelayMs;
    }

    /**
     * Attempts to connect to a JMS broker and makes continuous retries with an increasing interval if fails
     * When the maximum interval is reached, issues an error and keeps on trying
     * Sets the exception listener (a callback) in the created JMSContext which calls this method when the
     * connection with the broker goes down due to network issue or intentional connection termination
     */
    public void connectToBrokerWithRetries() {
        String connectionDetails = formatConnectionDetails();
        LOGGER.info("Attempting to connect to JMS broker '{}'. Connection details = {}", connectorName, connectionDetails);

        JMSContext context = null;
        int sleepingTimeMs = INITIAL_RECONNECTION_DELAY_MS;
        int accumulatedSleepingTimeMs = 0;

        // Try to reconnect until we succeed. IMPORTANT! This is a blocking loop that never ends so it must be run in a separate thread
        while (context == null) {
            try {
                context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE);
                LOGGER.info("Successfully connected to the JMS broker '{}'. Connection details = {}", connectorName, connectionDetails);

                boolean hadUnsuccessfulConnectionAttempts = accumulatedSleepingTimeMs > 0;
                if (hadUnsuccessfulConnectionAttempts) {
                    LOGGER.warn(
                        "Before this successful attempt, I spent {} ms repeatedly trying to connect to '{}'. Please check the broker's health. Connection details = {}",
                        accumulatedSleepingTimeMs, connectorName, connectionDetails
                    );
                }

                Destination destination = context.createQueue(QUEUE_PREIX + queueName);
                JMSConsumer jmsConsumer = context.createConsumer(destination);
                jmsConsumer.setMessageListener(messageListener);
                LOGGER.info("Successfully connected to the queue '{}' at '{}'. Connection details = {}", queueName, connectorName, connectionDetails);

                // Sets a callback that will be invoked when something happens with a connection to a broker
                context.setExceptionListener(
                    jmsException -> {
                        LOGGER.warn("Something bad happened to JMS broker connection to '{}'. I will try to reconnect. Connection details = {}", connectorName, connectionDetails);
                        connectToBrokerWithRetries();
                    }
                );
            } catch (Exception e) {
                LOGGER.warn(
                    "Failed to create a JMS context for '{}'. I will wait for {} ms and then make a reconnection attempt. Connection details = {}",
                    connectorName, sleepingTimeMs, connectionDetails, e
                );
                context = null;
                try {
                    Thread.sleep(sleepingTimeMs);
                    accumulatedSleepingTimeMs += sleepingTimeMs;
                    int doubledSleepingTime = sleepingTimeMs * 2;
                    // We double the sleeping time on each subsequent attempt until we hit the limit
                    // Then we just keep on reconnecting forever using the limit value
                    boolean nextReconnectionDelayWillExceedMaxDelay = doubledSleepingTime >= MAX_RECONNECTION_DELAY_MS;
                    if (nextReconnectionDelayWillExceedMaxDelay) {
                        sleepingTimeMs = MAX_RECONNECTION_DELAY_MS;
                        LOGGER.error(
                            "Repeatedly failed to create a JMS context for {} ms. I will keep on trying every {} ms but please check the broker availability. Connection details = {}",
                            accumulatedSleepingTimeMs, sleepingTimeMs, connectionDetails
                        );
                    } else {
                        sleepingTimeMs = doubledSleepingTime;
                    }
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }
    }

    private String formatConnectionDetails() {
        String connectionDetails = "[]";
        try {
            connectionDetails = String.format(
                "[ host = %s, port = %d, queueManager = %s, channel = %s, user = %s ]",
                connectionFactory.getStringProperty(WMQConstants.WMQ_HOST_NAME),
                connectionFactory.getIntProperty(WMQConstants.WMQ_PORT),
                connectionFactory.getStringProperty(WMQConstants.WMQ_QUEUE_MANAGER),
                connectionFactory.getStringProperty(WMQConstants.WMQ_CHANNEL),
                connectionFactory.getStringProperty(WMQConstants.USERID)
            );
        } catch (Exception e) {
            LOGGER.warn("Failed to get the connection details. This is not critical, but the details will be unavailable");
        }
        return connectionDetails;
    }
}

这是你如何使用它:

LOGGER.info("Starting the initial connection thread");
Thread cftInitialConnectionThread = new Thread(cftConnectionManager::connectToBrokerWithRetries);
cftInitialConnectionThread.start();

答案 2 :(得分:0)

这里有几点可以为您解决问题。

以下行设置一旦发现连接丢失,MQ将尝试重新连接到队列管理器的时间。

jcf.setIntProperty(WMQConstants.WMQ_CLIENT_RECONNECT_TIMEOUT, MQConnectionDetails.RECONNECT_TIMEOUT);

客户端需要多长时间才能注意到连接断开,取决于失败的类型,但是在您描述的情况下,它将基于客户端上HBINT通道的SVRCONN设置队列管理器。

当没有其他正常流量通过通道时,心跳每HBINT秒发送一次。通道的超时基于HBINT,如果HBINT小于60秒,则超时是HBINT的两倍,如果HBINT是60秒或较大的值是HBINT加60秒。超时是基于上次发送流量或发送心跳的时间,而不是基于更改防火墙配置的时间,尽管在这种情况下,它似乎是每秒钟发送一条消息,因此应该已经关闭。

基于日志,我不确定它是否选择了RECONNECT选项,因为我希望在重新连接超时到期时会收到以下任一错误,而不是MQRC_CONNECTION_BROKEN

  • MQRC_RECONNECT_FAILED
  • MQRC_RECONNECT_TIMED_OUT

在我拥有的示例中,我是这样设置的,也许可以尝试这种方式,而不是您当前设置的方式:

jcf.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT);
jcf.setClientReconnectTimeout(MQConnectionDetails.RECONNECT_TIMEOUT);

根据我的观察,我猜您的HBINTSVRCONN频道上设置为45秒,并且连接只是在MQRC_CONNECTION_BROKEN上以90秒超时而从未尝试重新连接。

相关问题