【问题标题】:IBM MQ: reconnect to message broker in case of connection error after startupIBM MQ:如果启动后出现连接错误,请重新连接到消息代理
【发布时间】:2020-01-08 21:46:01
【问题描述】:

我正在尝试在 IBM MQ 队列上发布消息。 这是我的实现 -

@Bean("jmsTemplate")
public JmsTemplate createProducer(@Qualifier("jmsConnectionFactory") ConnectionFactory cf) {
    JmsTemplate jmsTemplate = new JmsTemplate(cf);
    jmsTemplate.setDefaultDestinationName("my-queue-name");
    return jmsTemplate;
}

然后我在调度程序中调用它以每秒生成消息 -

@Autowired @Qualifier("jmsTemplate") JmsTemplate jmsTemplate;

@Scheduled(fixedDelayString = "1000")
public void runOnStart() {
    String message = "sample message "+String.valueOf(System.currentTimeMillis());
    jmsTemplate.convertAndSend(message);
    LOGGER.info(message);
}

一切正常。然后我关闭了互联网,这段代码开始抛出错误——说队列连接不可用。我将我的系统连接回互联网,它再次开始发送消息。凉爽的!它按我的意愿工作。

我尝试使用带有 javaContext 实现的 JMS2.0 重复相同的实验。这是我的第二个实现 -

@Bean("jmsContext")
public JMSContext createProducer(@Qualifier("jmsConnectionFactory") ConnectionFactory cf) {
    return cf.createContext();
}
@Bean("jmsProducer")
public JMSProducer createProducer(@Qualifier("jmsContext") JMSContext jmsContext) {
    return jmsContext.createProducer();
}

再次,与上一种方法类似,我创建了一个调度程序来发布这样的消息 -

@Autowired @Qualifier("jmsContext") JMSContext jmsContext;
@Autowired @Qualifier("jmsProducer") JMSProducer jmsProducer;

@Scheduled(fixedDelayString = "1000")
public void runOnStart() {
    try {
        Destination destination = this.jmsContext.createQueue("my-queue-name"));
        String message = "sample message "+String.valueOf(System.currentTimeMillis());
        this.jmsProducer.send(destination, message);
    } catch (JMSException e) {
        LOGGER.error("Error in sending message", e.getLinkedException());
    }
}

在这里,我也可以发送消息。到现在都很好。我的问题出现在以下部分。 调度程序正在运行,我断开了我的系统与互联网的连接,代码抛出错误说没有连接。我重新连接了我的系统,但我的消息仍然没有发送到代理(与之前的实现不同)。不是应该回连并发送消息吗?

我在第二次实施中遗漏了什么?

请注意:Bean @Qualifier("jmsConnectionFactory") ConnectionFactory cf 对于两个实现都是相同的,就像 -

public static ConnectionFactory getMQConnectionFactory (
        Map<String, String> queueDetails,
        SSLContext sslContext) throws Exception {
    MQConnectionFactory cf = new MQConnectionFactory();
    cf.setHostName(queueDetails.get("hostname"));
    cf.setPort(Integer.parseInt(queueDetails.get("port")));
    cf.setQueueManager(queueDetails.get("queueManager"));
    cf.setChannel(queueDetails.get("channel"));
    cf.setTransportType(WMQConstants.WMQ_CM_CLIENT);
    cf.setStringProperty(WMQConstants.USERID, queueDetails.get("username"));
    cf.setSSLCipherSuite(queueDetails.get("sslCipherSuite"));
    cf.setSSLSocketFactory(sslContext.getSocketFactory());
    return cf;
}

例外——

com.ibm.mq.MQException: JMSCMQ0001: IBM MQ call failed with compcode '2' ('MQCC_FAILED') reason '2009' ('MQRC_CONNECTION_BROKEN').

【问题讨论】:

  • 我猜,主要区别在于,在这种情况下,JmsTemplate 会为每个convertAndSend 重新连接,而 JMS 2.0 实现不会并且只是重新使用单个现有的联系。因此,与其将JMSContextJMSProducer 定义为bean,不如在需要时重新创建它们。

标签: spring spring-boot jms ibm-mq messaging


【解决方案1】:

如果您希望底层 MQ 客户端代码在失败时重新连接,您需要像这样启用 mq 自动重新连接:

cf.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT);
cf.setClientReconnectTimeout(1800); // how long in seconds to continue to attempt reconnection before failing 

【讨论】:

  • 根据此配置以阻塞方式尝试自动重新连接一次,持续 1800 秒。超时后,如果它没有再次获得连接,它将不再尝试,并且将继续每秒在计划的活动中抛出连接错误。我需要的是,应该每隔一段时间(比如 5 秒)连续尝试自动重新连接,直到 1 小时,或者说在放弃之前进行 50 次重新连接尝试。 JmsTemplate 似乎可以做到这一点,但@M. Deniam 在他对问题的评论中正确吗?在这种情况下,JmsTemplate 是否会为每个 convertAndSend 重新连接?
  • @Innovationchef 重新连接不会只尝试一次,它会每隔几秒尝试一次,直到指定的超时。如果在超时之后它仍然没有连接,它将失败并出现重新连接错误。如果您想要 60 分钟的重试,则将其提高到 3600。如果没有到您的目的地的网络路径,则需要根据操作系统默认设置在下一次之前超时每次 tcp 连接尝试,除非您也将其调低。
  • 查看此答案以获取有关超时活动连接(基于通道 HBINT)和连接尝试的更多详细信息。
  • 如果 SVRCONN 的 HBINT 为默认值 300,则初始通道超时可能需要 6 分钟。在此之后,当它尝试重新连接时,如果 TCP 流量被黑洞(如在您的情况下,您“将我的系统与 Internet 断开连接”,或服务器崩溃),连接尝试将根据操作系统默认值超时(例如 RHEL 上 180 多秒) )。
猜你喜欢
  • 1970-01-01
  • 2020-04-23
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
  • 1970-01-01
  • 2018-07-07
  • 1970-01-01
  • 2018-05-29
相关资源
最近更新 更多