Asynchronous 如何使用请求-应答模式在同步调用中包装JMS到WebSphere MQ桥?

Asynchronous 如何使用请求-应答模式在同步调用中包装JMS到WebSphere MQ桥?,asynchronous,jms,weblogic,integration,ibm-mq,Asynchronous,Jms,Weblogic,Integration,Ibm Mq,我正在处理一个新的情况,我相信这可能是一些人的共同点:) 根据需求,我需要构建一个类似于web服务调用的同步在线事务的用户体验,它实际上使用异步JMS-MQ桥将调用委托给IBM MQ系列 客户端调用web服务,然后他的消息应发布在App server上的JMS队列中,该队列将被传递到WebSphere MQ,然后在处理后,响应将被传递回固定JMS队列端点中的App server 该需求处理这个事务,如果WebSphereMQ没有在定义的时间内交付响应,那么web服务应该向客户端发送一个超时信号并

我正在处理一个新的情况,我相信这可能是一些人的共同点:)

根据需求,我需要构建一个类似于web服务调用的同步在线事务的用户体验,它实际上使用异步JMS-MQ桥将调用委托给IBM MQ系列

客户端调用web服务,然后他的消息应发布在App server上的JMS队列中,该队列将被传递到WebSphere MQ,然后在处理后,响应将被传递回固定JMS队列端点中的App server

该需求处理这个事务,如果WebSphereMQ没有在定义的时间内交付响应,那么web服务应该向客户端发送一个超时信号并忽略这个事务,那么这个事务将需要超时

问题的概要如下

我需要阻止web服务上的请求,直到响应到达或超时

我正在寻找一些开放的图书馆来帮助我完成这项任务。 或者唯一的解决方案是阻塞线程并保持响应池? 也许我可以用一个监听器来实现一些块,当响应到达时会得到通知

一点讨论对我现在试图澄清我在这方面的想法非常有帮助。 有什么建议吗

我有一张素描,希望能有助于澄清问题;)


经过几天的编码,我找到了解决方案。我正在使用带有JAX-WS注释和标准JMS的标准EJB3

到目前为止,我为满足需求而编写的代码如下。它是一个无状态会话Bean,具有Bean管理事务(BMT),因为使用standart容器管理事务(CMT)会导致某种挂起,我相信,因为我试图将两个JMS交互放在同一个事务中,因为它们在同一个方法中,所以请注意,我必须为每个与JMS队列的交互启动和完成事务。我使用weblogic来解决这个问题。我还编写了一个MDB,它基本上使用来自队列端点jms/Pergunta的消息,并在jms/Resposta队列上放置一条响应消息。我这样做是为了模拟问题MQ端的预期行为。实际上,在真实的场景中,我们可能会在大型机上有一些COBOL应用程序,甚至还有其他java应用程序处理消息并将响应放在响应队列上

如果有人需要尝试这段代码,那么基本上您只需要拥有一个容器J2EE5,并使用jndi名称配置2个队列:jms/Pergunta和jms/Resposta

EJB/Webservice代码:

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
@WebService(name="DJOWebService")
public class DJOSessionBeanWS implements DJOSessionBeanWSLocal {

    Logger log = Logger.getLogger(DJOSessionBeanWS.class.getName());

    @Resource
    SessionContext ejbContext;

    // Defines the JMS connection factory.
    public final static String JMS_FACTORY = "weblogic.jms.ConnectionFactory";

    // Defines request queue
    public final static String QUEUE_PERG = "jms/Pergunta";

    // Defines response queue
    public final static String QUEUE_RESP = "jms/Resposta";


    Context ctx;
    QueueConnectionFactory qconFactory;

    /**
     * Default constructor. 
     */
    public DJOSessionBeanWS() {
        log.info("Construtor DJOSessionBeanWS");
    }

    @WebMethod(operationName = "processaMensagem")
    public String processaMensagem(String mensagemEntrada, String idUnica)
    {
        //gets UserTransaction reference as this is a BMT EJB.
        UserTransaction ut = ejbContext.getUserTransaction();
        try {

            ctx = new InitialContext();
            //get the factory before any transaction it is a weblogic resource.
            qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
            log.info("Got QueueConnectionFactory");
            ut.begin();
            QueueConnection qcon = qconFactory.createQueueConnection();
            QueueSession qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
            Queue qs = (Queue) (new InitialContext().lookup("jms/Pergunta"));
            TextMessage message = qsession.createTextMessage("this is a request message");
            message.setJMSCorrelationID(idUnica);
            qsession.createSender(qs).send(message);
            ut.commit();
            qcon.close();
            //had to finish and start a new transaction, I decided also get new references for all JMS related objects, not sure if this is REALLY required
            ut.begin();
            QueueConnection queuecon = qconFactory.createQueueConnection();
            Queue qreceive = (Queue) (new InitialContext().lookup("jms/Resposta"));
            QueueSession queuesession = queuecon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
            String messageSelector = "JMSCorrelationID = '" + idUnica + "'";
            //creates que receiver and sets a message selector to get only related message from the response queue.
                    QueueReceiver qr = queuesession.createReceiver(qreceive, messageSelector);
            queuecon.start();
            //sets the timeout to keep waiting for the response...
            TextMessage tresposta = (TextMessage) qr.receive(10000);
            if(tresposta != null)
            {
                ut.commit();
                queuecon.close();
                return(tresposta.toString());
            }
            else{
                //commints anyway.. does not have a response though 
                ut.commit();
                queuecon.close();
                log.info("null reply, returned by timeout..");
                return "Got no reponse message.";
            }



        } catch (Exception e) {
            log.severe("Unexpected error occurred ==>> " + e.getMessage());
            e.printStackTrace();
            try {
                ut.commit();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return "Error committing transaction after some other error executing ==> " + e.getMessage();
        } 

    }
}   
这是MDB的代码,它模拟了这个问题的MQ端。在测试过程中,我有一个Thread.sleep片段来模拟和测试客户端的超时,以验证解决方案,但在这个版本中它不存在

/**
 * Mock to get message from request queue and publish a new one on the response queue.
 */
@MessageDriven(
        activationConfig = { @ActivationConfigProperty(
                propertyName = "destinationType", propertyValue = "javax.jms.Queue"
        ) }, 
        mappedName = "jms/Pergunta")
public class ConsomePerguntaPublicaRespostaMDB implements MessageListener {

    Logger log = Logger.getLogger(ConsomePerguntaPublicaRespostaMDB.class.getName());

    // Defines the JMS connection factory.
    public final static String JMS_FACTORY = "weblogic.jms.ConnectionFactory";

    // Define Queue de resposta
    public final static String QUEUE_RESP = "jms/Resposta";


    Context ctx;
    QueueConnectionFactory qconFactory;



    /**
     * Default constructor. 
     */
    public ConsomePerguntaPublicaRespostaMDB() {
        log.info("Executou construtor ConsomePerguntaPublicaRespostaMDB");
        try {
            ctx = new InitialContext();
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    /**
     * @see MessageListener#onMessage(Message)
     */
    public void onMessage(Message message) {
        log.info("Recuperou mensagem da fila jms/FilaPergunta, executando ConsomePerguntaPublicaRespostaMDB.onMessage");
        TextMessage tm = (TextMessage) message;

        try {
            log.info("Mensagem recebida no onMessage ==>> " + tm.getText());

            //pega id da mensagem na fila de pergunta para setar corretamente na fila de resposta.
             String idMensagem = tm.getJMSCorrelationID();
             log.info("Id de mensagem que sera usada na resposta ==>> " + idMensagem);

            qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
            log.info("Inicializou contexto jndi e deu lookup na QueueConnectionFactory do weblogic com sucesso. Enviando mensagem");
            QueueConnection qcon = qconFactory.createQueueConnection();
            QueueSession qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
            Queue queue = (Queue) (ctx.lookup("jms/Resposta"));
            TextMessage tmessage = qsession.createTextMessage("Mensagem jms para postar na fila de resposta...");
            tmessage.setJMSCorrelationID(idMensagem);
            qsession.createSender(queue).send(tmessage);
        } catch (JMSException e) {
            log.severe("Erro no onMessage ==>> " + e.getMessage());
            e.printStackTrace();
        }  catch (NamingException e) {
            log.severe("Erro no lookup ==>> " + e.getMessage());
            e.printStackTrace();
        }

    }

}

[]s

嘿,感谢您发布自己的解决方案

是的,在这种情况下,带超时的receive()是最优雅的方式

当心由于超时而无法读取的消息会发生什么情况。如果您的客户机再次访问同一队列,他可能会收到一条过时的消息

确保及时删除超时的消息(如果没有其他原因,则不要用未处理的消息填充队列)

您可以通过代码(设置消息生成器上的生存时间)或Websphere MQ服务器(使用消息自动过期的队列)轻松地完成这项工作


如果您不能/不想修改代码的MQ端,则后者更容易。这就是我要做的:)

是的,我们有一个错误队列,其中超时消息被转发,另一段代码处理它们。