【问题标题】:Response getting back from the ibm mq is not match for the request sometimes从 ibm mq 返回的响应有时与请求不匹配
【发布时间】:2019-05-30 23:58:42
【问题描述】:

我复制了如何调用 IBM MQ 的 java 客户端代码,并将请求传递给队列,但有时我从队列中得到错误的响应。

例如,如果我提交以下请求: F LOYFI6331760101046481882

我期望得到我应该得到的回应 F LOYFA36331760101046481882

但实际上我得到了 F LOYFA36331760101051292448

如您所见,卡号是错误的。

这里是代码

import javax.jms.BytesMessage;
import javax.jms.Destination;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSProducer;
import javax.jms.TextMessage;

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

public class MQClient {
    // System exit status value (assume unset value to be 1)
    private static int status = 1;

    public static byte[] sendAndReceive(String HOST, Integer PORT, String QMGR, String CHANNEL, String requestQueue, String responseQueue, String payload) {
        // Variables
        JMSContext context = null;
        Destination destination = null;
        JMSProducer producer = null;
        JMSConsumer consumer = null;
        BytesMessage receivedMessage = null;
        byte[] result = null;
        try {
            // Create a connection factory
            JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
            JmsConnectionFactory cf = ff.createConnectionFactory();

            // Set the properties
            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, QMGR);
            cf.setStringProperty(WMQConstants.WMQ_APPLICATIONNAME, "JmsPutGet (JMS)");
            cf.setStringProperty(WMQConstants.WMQ_TARGET_CLIENT, "1");
            // Create JMS objects
            context = cf.createContext();
            destination = context.createQueue("queue:///" + requestQueue +"?targetClient=1");

            TextMessage message = context.createTextMessage(payload);

            producer = context.createProducer();
            producer.send(destination, message);
            System.out.println("Sent message:\n" + message);

            destination = context.createQueue("queue:///" + responseQueue + "?targetClient=1");
            consumer = context.createConsumer(destination); // autoclosable
            receivedMessage= (BytesMessage)consumer.receive();
            System.out.println("Receiving message:" + receivedMessage);
            int text_length = new Long(receivedMessage.getBodyLength()).intValue();
            result = new byte[text_length];
            receivedMessage.readBytes(result, text_length);

            System.out.println("\nReceived message:\n" + new String(result));

            recordSuccess();

        } catch (JMSException jmsex) {
            recordFailure(jmsex);
        }finally {
            context.close();
        }

        return result;

    }
}

我有另一个项目要同时运行以调用MQClient.sendAndReceive() 方法,具有相同的hostportQMGRchannelrequestQueueresponseQueue,只有payload 不同。

那么我该如何修复上面的代码,以确保我总是得到与请求对应的正确响应?

编辑: 1. 对于 JoshMac 的问题,app 是指 IBM MQ 吗?或者会调用我的sendAndReceive 函数的应用程序?

  1. 这是我的流程,我使用 mule 流程从 POS 获取请求,处理请求,这需要调用 IBM MQ(位于 AS400 上),从 MQ 获取响应,然后发送回POS。 (在这个例子中,我需要将我的请求提交给INQ1 并从INQR1 获得响应)。根据下面的答案,似乎sendAndReceive 函数被视为Requester,我需要另一个流程来调用Responder 来处理响应,所以receivedMessage= (BytesMessage)consumer.receive(); 不会卡住?如果我错了,请纠正我

【问题讨论】:

  • 你能用不同的主题来区分吗?
  • @yuanqingfei 可以举个例子吗?
  • 这是一个消耗多个主题的示例:stackoverflow.com/questions/35715767/…希望对您有所帮助
  • 响应的应用是否将关联ID设置为原始消息ID?如果是这样,您可以通过相关 ID 获取。
  • 响应的应用是否将关联ID设置为原始消息ID?

标签: java multithreading ibm-mq mule4


【解决方案1】:

你能用不同的主题来区分吗?

在进行点对点消息传递时,这是个坏主意。

destination = context.createQueue("queue:///" + responseQueue + "?targetClient=1");

听起来您的 responseQueue 在多个消费者之间共享。你有两个选择:

  1. 创建您自己的临时动态队列并将其设置为“回复” 排队

Queue replyQ = session.createTemporaryQueue();
  1. 使用 MsgId / CorrelId 请求-回复消息模式。

即遵循此页面上的建议:Not able to get response from IBM MQ using JMS application

【讨论】:

  • 我不知道如何正确设置临时队列,因为我需要将请求发送到 INQ1,然后我得到 INQR1 的响应..
【解决方案2】:

看起来所有响应都发送到 INQR1。为了帮助您的应用程序识别哪些响应是针对它的以及它可以忽略哪些响应,您可以使用选择器,通常选择器是通过相关 ID 进行的,尽管您可以使用其他字段。

本质上,您正在使用有 JMS 示例的请求/响应模式 - https://github.com/ibm-messaging/mq-dev-patterns/tree/master/JMS

您的请求者的逻辑如下:

String correlationID = String.format("%24.24s", UUID.randomUUID().toString());
message.setJMSCorrelationIDAsBytes(b);

生成关联 ID,并将其添加到消息中。

然后,您的请求者将创建一个基于关联 ID 的选择器来过滤响应队列:

try {
      b = correlationID.getBytes();
      selector = "JMSCorrelationID='ID:" + getHexString(b) + "'";
    } catch (Exception e) {
       ...
    }

你用来构造你的消息消费者:

JMSConsumer consumer = context.createConsumer(requestQueue, selector);
Message receivedMessage = consumer.receive();

当您的响应者收到初始请求时,它可以获取相关 id:

String correlationID = receivedMessage.getJMSCorrelationID();

并使用它来设置响应的相关 ID:

message.setJMSCorrelationID(correlationID);

【讨论】:

  • 我还是很困惑,如何确保将请求发送到 INQ1 并从 INQR1 获得响应?并且基于 github 示例,requestQueue 是临时队列,而不是 INQR1...
  • 这在您的代码中保持不变。如果您有响应队列,则不需要临时队列,只需使用响应队列即可。您已经拥有 destination = context.createQueue("queue:///" + requestQueue +"?targetClient=1");destination = context.createQueue("queue:///" + responseQueue + "?targetClient=1"); 它们保持不变。唯一的区别是您如何在响应队列上创建消费者,在您的情况下将变为 JMSConsumer consumer = context.createConsumer(destination, selector);
  • 试过了,现在卡在receivedMessage = (BytesMessage)consumer.receive();
  • 所描述的内容要求响应应用程序将相关 id 从您的请求消息复制到回复的相关 id。根据我的经验,更常见的模式是将请求消息的消息 id 复制到回复的相关 id。我问过这个问题,但你没有回答这个问题。
  • 所以现在您的请求者正在等待带有匹配选择器的消息。您的响应者需要设置适当的选择器,在本例中为相关 ID。正如@JoshMc 所说,常见模式是使用原始请求消息的消息 id 作为相关 id,选择器会利用它。这意味着您不需要生成相关 ID。无论哪种方式,您的请求者和响应者都需要同步并使用相同的选择器/值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-14
  • 2020-12-01
  • 2014-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多