Ответ, получаемый от ibm mq, иногда не соответствует запросу

Я копирую клиентский код Java для вызова IBM MQ и передаю запрос в очередь, но иногда получаю неправильный ответ из очереди.

Например, если я отправлю следующий запрос: 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(), с теми же host, port, QMGR, channel, requestQueue и responseQueue, только payload отличается.

Итак, как мне исправить приведенный выше код, чтобы убедиться, что я всегда получаю правильный ответ, соответствующий запросу?

РЕДАКТИРОВАТЬ: 1. Для вопросов JoshMac приложение означает IBM MQ? Или приложение, которое будет вызывать мою функцию sendAndReceive?

  1. Вот поток, который у меня есть: я использую поток мулов, принимаю запрос от POS, обрабатываю запрос, который нужно вызвать IBM MQ (который находится на AS400), чтобы получить ответ от MQ и отправить обратно в POS. (В этом примере мне нужно отправить запрос INQ1 и получить ответ от INQR1). Судя по приведенному ниже ответу, кажется, что функция sendAndReceive обрабатывается как Requester, мне нужен другой поток для вызова Responder для обработки ответа, поэтому receivedMessage= (BytesMessage)consumer.receive(); не застрянет? Поправьте меня, если я ошибаюсь

person Vincent Zheng    schedule 28.05.2019    source источник
comment
Можете ли вы использовать другую тему, чтобы различать?   -  person Qingfei Yuan    schedule 28.05.2019
comment
@yuanqingfei, можешь привести пример?   -  person Vincent Zheng    schedule 28.05.2019
comment
Вот пример, который использует несколько тем: stackoverflow.com/questions/35715767 / Надеюсь, это поможет   -  person Qingfei Yuan    schedule 28.05.2019
comment
Устанавливает ли отвечающее приложение идентификатор корреляции на исходный идентификатор сообщения? Если это так, вы можете получить идентификатор корреляции.   -  person JoshMc    schedule 28.05.2019
comment
Устанавливает ли отвечающее приложение идентификатор корреляции на исходный идентификатор сообщения?   -  person JoshMc    schedule 29.05.2019
comment
я обновил свой вопрос   -  person Vincent Zheng    schedule 30.05.2019


Ответы (2)


Можете ли вы использовать другую тему, чтобы различать?

Это плохая идея, когда вы делаете двухточечный обмен сообщениями.

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

Похоже, что ваш responseQueue используется несколькими потребителями. У вас есть 2 варианта:

  1. Создайте свою собственную временную динамическую очередь и установите ее как очередь «Ответить»

i.e.

Queue replyQ = session.createTemporaryQueue();
  1. Используйте шаблон обмена сообщениями запрос-ответ MsgId / CorrelId.

т. е. следуйте рекомендациям на этой странице: Не удалось чтобы получить ответ от IBM MQ с помощью приложения JMS

person Roger    schedule 28.05.2019
comment
Я не знаю, как правильно настроить временную очередь, потому что мне нужно отправить запрос на INQ1, и я получаю ответ от INQR1. - person Vincent Zheng; 28.05.2019

Похоже, все ответы отправляются на INQR1. Чтобы помочь вашему приложению определить, какие ответы предназначены для него, а какие оно может игнорировать, вы можете использовать селектор, и обычно селектор осуществляется по идентификатору корреляции, хотя вы можете использовать и другие поля.

По сути, вы используете шаблон запроса/ответа, для которого есть образцы JMS — https://github.com/ibm-messaging/mq-dev-patterns/tree/master/JMS

У вашего запрашивающего будет такая логика:

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

для создания идентификатора корреляции и добавления его в сообщение.

Затем ваш запрашивающий создаст селектор на основе идентификатора корреляции для фильтрации очереди ответов:

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

который вы используете для создания своего потребителя сообщений:

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

Когда ваш ответчик получает первоначальный запрос, он может получить идентификатор корреляции:

String correlationID = receivedMessage.getJMSCorrelationID();

и используйте его, чтобы установить идентификатор корреляции для ответа:

message.setJMSCorrelationID(correlationID);
person chughts    schedule 29.05.2019
comment
Я все еще не понимаю, как убедиться, что я отправляю свой запрос в INQ1 и получаю ответ от INQR1? И, основываясь на примере github, requestQueue - это временная очередь, а не INQR1... - person Vincent Zheng; 29.05.2019
comment
Это остается неизменным в вашем коде. Если у вас есть очередь ответов, то вам не нужна временная очередь, просто используйте очередь ответов. У вас уже есть destination = context.createQueue("queue:///" + requestQueue +"?targetClient=1"); и destination = context.createQueue("queue:///" + responseQueue + "?targetClient=1"); Они остаются прежними. Единственная разница будет заключаться в том, как вы создаете потребителя в очереди ответов, которая в вашем случае станет JMSConsumer consumer = context.createConsumer(destination, selector); - person chughts; 29.05.2019
comment
Пробовал, а теперь зависает на receivedMessage = (BytesMessage)consumer.receive(); - person Vincent Zheng; 29.05.2019
comment
Для того, что описано, требуется, чтобы отвечающее приложение скопировало идентификатор корреляции из вашего сообщения запроса в идентификатор корреляции ответа. По моему опыту, более распространенным шаблоном является то, что идентификатор сообщения запроса копируется в идентификатор корреляции ответа. Я спрашивал об этом, но вы не ответили на этот вопрос. - person JoshMc; 30.05.2019
comment
Итак, теперь ваш запросчик ожидает сообщений с соответствующим селектором. Ваш ответчик должен установить соответствующий селектор, в данном случае идентификатор корреляции. Как говорит @JoshMc, общий шаблон заключается в использовании идентификатора сообщения исходного сообщения запроса в качестве идентификатора корреляции, и селектор будет использовать это. Это означает, что вам не нужно генерировать идентификатор корреляции. В любом случае ваш запросчик и ответчик должны синхронизироваться и использовать один и тот же селектор/значение. - person chughts; 30.05.2019