【问题标题】:Transaction demarcation when sending a JMS message using EJB 3使用 EJB 3 发送 JMS 消息时的事务划分
【发布时间】:2013-09-05 10:54:42
【问题描述】:

在我的 Java EE 应用程序中,我将异步 DB 记录器实现为 MDB,它通过 JMS 接收 XML 消息并将它们写入 DB。

在另一个 MDB 中,我创建了一条日志消息,并使用以下代码将它们发送到记录器 MDB 的输入队列:

    public static void log(String correlId, String message, String data) throws Exception{

            SysLogEntry sysLogEntry = new SysLogEntry();
            sysLogEntry.setCorrelId(correlId);
            sysLogEntry.setDatetimeCreate(new Date());
            sysLogEntry.setMessage(message);
            sysLogEntry.setData(data);

            ConnectionFactory jmsConnectionFactory = (ConnectionFactory)initialContext.lookup(JMS_CONNECTION_FACTORY_JNDI_NAME);
            Destination logEventDestination = (Destination) initialContext.lookup(LOG_EVENT_DESTINATION_JNDI_NAME);

            JmsUtils.sendMsgToDestination(JaxbUtils.toString(sysLogEntry, jaxbContext), jmsConnectionFactory, logEventDestination, false, Session.AUTO_ACKNOWLEDGE);

        }

public static void sendMsgToDestination(String payload, ConnectionFactory connFactory, Destination destination, boolean sessionTransacted, int acknowledgeMode) throws JMSException{
        if(payload == null)
            throw new IllegalArgumentException("Message payload is null");
        if(connFactory == null)
            throw new IllegalArgumentException("Connection factory is null");
        if(destination == null)
            throw new IllegalArgumentException("Message destination is null");

        Connection connection = null;
        try{
            connection = connFactory.createConnection();
            Session session = connection.createSession(sessionTransacted, acknowledgeMode);
            MessageProducer messageProducer = session.createProducer(destination);
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText(payload);
            messageProducer.send(textMessage);
        } finally {
            if(connection != null){
                try{
                    connection.close();
                } catch (JMSException ignore){

                }
            }
        }
    }

在哪里

  • SysLogEntry 是一个带有 JAXB 注解的类,我用它来进行序列化
  • JMS_CONNECTION_FACTORY_JNDI_NAME 是 XA 连接工厂的 JNDI 名称

但是,每次我创建日志消息的事务回滚时,日志消息都不会放入记录器输入队列。

谁能告诉我我的代码有什么问题?我曾考虑使用一个单独的无状态会话 bean,它会启动一个新事务来生成日志消息,但这种方法有一个缺点:在 EJB 中,我可以轻松地让容器注入记录器 bean,但我也有非 EJB我想在其中进行异步日志记录的对象。

我的应用服务器是 Weblogic 10.3.3

【问题讨论】:

    标签: logging ejb jms ejb-3.0 message-driven-bean


    【解决方案1】:

    您可以将包含静态日志方法的类的静态成员分配给无状态 EJB 代理的实例。代理本身只是将调用路由到实例池中的空闲 bean,因此可以静态共享。

    但是...开始一个新的交易只是为了逃避当前的交易听起来很浪费。尤其是如果频繁地记录到这个异步记录器,您可能想要以不同的方式进行。

    使用带有几个线程的简单 Java SE 执行器池,然后将工作提交给它(一个 Runnable)。在这个可运行文件中,您仍然可以发送 JMS 消息,并保持现有代码不变。当池中的线程接手工作时,事务上下文会丢失,但这正是您首先需要的。

    【讨论】:

      【解决方案2】:

      如果出现系统异常(或sessionContext.setRollbackOnly),全局事务将由容器回滚(如果您使用容器管理事务)。 这意味着在全局事务中登记的 XA 感知资源上的任何操作都会回滚。

      在您的情况下,这包括您要发送给记录器的待处理消息。这个被删除了,因为事务被回滚了。


      在您的情况下,如果您使用 非 XA 连接工厂,这应该足够了。这样消息应该立即发送(如果记录器甚至有帮助)。因此,发送仅在跨越 JMS 发送任务的本地事务中运行。

      至于非 EJB 对象:您可以使用非 XA 连接工厂将发送功能提取到普通 Java 类(= 不是 EJB)中,并从 EJB 和普通类中使用它。

      【讨论】:

      • 你是对的。我认为如果我使用 sessionTransacted=false 和 acknowledgeMode=Session.AUTO_ACKNOWLEDGE 创建会话,则消息不会回滚。但似乎这些参数在容器管理的事务中被忽略了。使用非 XA 连接工厂按预期工作。谢谢。
      【解决方案3】:

      我会创建一个单独的 BEAN 来记录 Message 并且会 注释 sendMessage 以启动自己的独立事务:

      @Stateless
      public class SenderBean  {
      
      @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
      public sendMsgToDestination(String payload, ConnectionFactory connFactory, Destination destination, boolean sessionTransacted, int acknowledgeMode) throws JMSException{
              ...
              Connection connection = null;
              try(
                 connection = connFactory.createConnection();
                 Session session = connection.createSession(sessionTransacted, acknowledgeMode);
              ){
                  MessageProducer messageProducer = session.createProducer(destination);
                  TextMessage textMessage = session.createTextMessage();
                  textMessage.setText(payload);
                  messageProducer.send(textMessage);
              }
          }
      

      然后我将使用此 BEAN 进行日志记录,即使您的调用方事务回滚,它也会将消息写入队列

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-09
        • 1970-01-01
        相关资源
        最近更新 更多