事务环境中的JMS请求/响应模式 [英] JMS request/response pattern in transactional environment

查看:181
本文介绍了事务环境中的JMS请求/响应模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个EJB模块,必须通过TemporaryQueue与其他模块中的MDB同步交换消息。 EJB容器(在我的例子中是Glassfish 4.0)采用事务环境,我必须使用BEAN管理的事务,并使用UserTransaction对象来表示事务的开始和结束。



我的代码大纲是这样的:

  @Stateless 
@TransactionManagement(TransactionManagementType.BEAN)
public class CommunicationJMSUtils {
@Resource
private UserTransaction ut;
@Inject
private JMSContext context;
@Resource(lookup =jms / DestinationQueue)
private Queue destination;
private static final long JMS_COMMUNICATION_TIMEOUT = 5000;

public Map< String,String> getClientordersData(String id)throws JMSException {

try {
MapMessage mm = context.createMapMessage();
ut.begin();
TemporaryQueue replyQueue = context.createTemporaryQueue();

mm.setJMSReplyTo(replyQueue);

mm.setStringProperty(< ...>);
< ...>
mm.setString(< ...>);
< ...>

context.createProducer()。send(destination,mm);
ut.commit();

ut.begin();
ObjectMessage om =(ObjectMessage)context.createConsumer(replyQueue).receive(JMS_COMMUNICATION_TIMEOUT);
ut.commit();

if(om!= null){
return om.getBody(Map.class);
} else {
throw new JMSException(Failed to receive reply within+ JMS_COMMUNICATION_TIMEOUT);
}
} catch(NotSupportedException | RollbackException | HeuristicMixedException | HeuristicRollbackException | SecurityException | IllegalStateException | SystemException ex){
< ...>
}
}
}

第一个问题是时间到这个代码(接收部分)失败,例外

  MQJMSRA_DS4001:_checkDestination:不属于父连接的临时目标Id = 1760697170479431168 

虽然显然TemporaryQueue是使用相同的JMSContext创建的。



<第二个问题是这个代码的脆弱。如果我将context.createMapMessage()放在第一个事务中,或者从第一个事务中移动TemporaryQueue创建,这个代码片断肯定会失败。



不幸的是,JMS教程/文档不真正涵盖了特定的用例。在Java EE中使用JMS 2.0实现JMS请求/响应模式的正确方法是什么?

解决方案


  • 同步行为和(异步)消息使用JMS冲突不知何故,因为消息传递本质上是异步的。而不是使用JMS,只需调用方法,例如在本地或远程EJB上:无状态会话bean,MDB可以调用相同的功能(服务类),但是这样就可以提供两种不同类型的接口(sync&异步)


  • 你甚至创建一个调用方法的临时队列,这增加了复杂性(这导致麻烦)。

    / li>
  • UserTransaction 应该跨所有操作。应该只有一个 begin commit (和 rollback )。我看不到一个真正的交易边界,第一个TX的资源在第二个TX( replyQueue )中使用。我希望在第一个提交期间删除临时队列。


  • 可能相关:EJB与 TransactionManagementType.BEAN 充当交易屏障。它可能会暂停当前的交易:为什么EJB bean与bean管理的交易作为交易障碍?这里




所以,我建议添加一个提供相同的无状态会话bean,而不是修复事务处理功能,并称之为(同步)。在这种情况下,我没有看到为什么这不应该与容器管理的事务一起工作,因此它将是真正的跨边界交易。


I have an EJB module that has to synchronously exchange messages with a MDB in another module via TemporaryQueue. The EJB container (in my case it's Glassfish 4.0) assumes transactional environment, do I have to use BEAN-managed transaction and denote start and end of transactions using UserTransaction object.

The outline of my code is like this:

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class CommunicationJMSUtils {
    @Resource
    private UserTransaction ut;
    @Inject
    private JMSContext context;
    @Resource(lookup = "jms/DestinationQueue")
    private Queue destination;
    private static final long JMS_COMMUNICATION_TIMEOUT = 5000;

    public Map<String, String> getClientordersData(String id) throws JMSException {

    try {
            MapMessage mm = context.createMapMessage();
            ut.begin();
            TemporaryQueue replyQueue = context.createTemporaryQueue();

            mm.setJMSReplyTo(replyQueue);

            mm.setStringProperty(<...>);
            <...>   
            mm.setString(<...>);
            <...>

            context.createProducer().send(destination, mm);
            ut.commit();

            ut.begin();
            ObjectMessage om = (ObjectMessage) context.createConsumer(replyQueue).receive(JMS_COMMUNICATION_TIMEOUT);
            ut.commit();

            if (om != null) {
                return om.getBody(Map.class);
            } else {
                throw new JMSException("Failed to receive reply within " + JMS_COMMUNICATION_TIMEOUT);
            }
        } catch (NotSupportedException | RollbackException | HeuristicMixedException | HeuristicRollbackException | SecurityException | IllegalStateException | SystemException ex) {
        <...>
        }
    }
}

The first problem is that from time to time this code (receive part) fails with exception

    MQJMSRA_DS4001: _checkDestination:Temporary Destination not owned by  parent connectionId=1760697170479431168

although evidently TemporaryQueue is created with same JMSContext.

And the second problem is the "fragility" of this code. If I put context.createMapMessage() inside the first transaction or move TemporaryQueue creation out of the first transaction this snippet will definately fail.

Unfortunately, JMS tutorials/documentation do not really cover that particular use-case. What is the correct way to implement the JMS request/response pattern with JMS 2.0 in Java EE?

解决方案

  • Synchronous behaviour and (asynchronous) messaging using JMS conflict somehow, as messaging is asynchronous by nature. Instead of using JMS, just call the method, for example on a local or remote EJB: A stateless session bean and an MDB can call the same functionality (service class), but this way they provide two different types of interfaces (sync & async).

  • You are even creating a temporary queue for calling a method, this does add complexity (and this is causing trouble).

  • The UserTransaction should span all operations. There should be only one begin, and commit (and rollback). I cannot see a real transaction boundary, resources from the first TX are used in the 2nd TX (replyQueue). I would expect that the temporary queue was deleted during the first commit.

  • Possibly related: An EJB with TransactionManagementType.BEAN acts as a "transaction barrier". It will possibly suspend a current transaction: Why do EJB beans with bean-managed transactions act as a "transacation barrier"? here

So instead of fixing the transaction handling I suggest adding a stateless session bean which offers the same functionality, and call this one (synchronously). In that case I do not see a reason why this should not work with container managed transactions, and thus it will be really transactional across bean boundaries.

这篇关于事务环境中的JMS请求/响应模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆