Asynchronous 如何使用请求-应答模式在同步调用中包装JMS到WebSphere MQ桥?
我正在处理一个新的情况,我相信这可能是一些人的共同点:) 根据需求,我需要构建一个类似于web服务调用的同步在线事务的用户体验,它实际上使用异步JMS-MQ桥将调用委托给IBM MQ系列 客户端调用web服务,然后他的消息应发布在App server上的JMS队列中,该队列将被传递到WebSphere MQ,然后在处理后,响应将被传递回固定JMS队列端点中的App server 该需求处理这个事务,如果WebSphereMQ没有在定义的时间内交付响应,那么web服务应该向客户端发送一个超时信号并忽略这个事务,那么这个事务将需要超时 问题的概要如下 我需要阻止web服务上的请求,直到响应到达或超时 我正在寻找一些开放的图书馆来帮助我完成这项任务。 或者唯一的解决方案是阻塞线程并保持响应池? 也许我可以用一个监听器来实现一些块,当响应到达时会得到通知 一点讨论对我现在试图澄清我在这方面的想法非常有帮助。 有什么建议吗 我有一张素描,希望能有助于澄清问题;)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服务应该向客户端发送一个超时信号并
经过几天的编码,我找到了解决方案。我正在使用带有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端,则后者更容易。这就是我要做的:)是的,我们有一个错误队列,其中超时消息被转发,另一段代码处理它们。