【问题标题】:How to handle transaction rollback with JMS and JPA in a Java EE environment?如何在 Java EE 环境中使用 JMS 和 JPA 处理事务回滚?
【发布时间】:2015-05-08 21:15:40
【问题描述】:

CMT MDB 的默认回滚行为是将消息返回到目标,以便再次处理它。

即使事务回滚,是否可以避免重新传递由托管 MDB 处理的消息? (或者配置容器处理的确认行为)。

到目前为止,我想出了以下替代方案:

  1. 将业务事务与消息事务隔离 - 我可以在业务方法上使用TransactionAttributeType.REQUIRES_NEW,但它会创建一个业务场景 事件可能被处理两次,因为消息可能 业务交易成功后不予确认。
  2. 使用 BMT - 与上述相同的问题,因为事务将与消息事务分开。
  3. 使用 JMS Server 专有配置处理交付失败 - 如果可能,我希望将此逻辑保留在应用程序中。 由于WebLogic默认设置,我还必须为所有队列处理它 config 是永远重新传递消息。

阅读this tutorial 后,我仍然不确定如何解决这个问题,但到目前为止,使用专有的 WebLogic 控制台控制消息传递失败似乎是正确的选择。在这种情况下,对队列的重新投递设置一个限制 - 例如:3 次尝试。由于无效的业务事件可能会全部失败 3 次,因此会产生处理开销,但我可以保证系统的完整性。

你们觉得呢?

详情

我有一个与业务事务集成的 MDB,它使用 JPA(WebLogic 10.3.6 中的 EclipseLink)。一切都使用 CMT 运行,事务是分布式的。事务和消息确认由容器控制。

如果 JPA 提供程序中发生异常(例如:非空列的空值),则将重新传递消息,因为提供程序正在回滚事务并且未确认消息。我是否捕捉到异常并不重要,EclipseLink 无论如何都会回滚事务。我知道这是 JPA 的正确行为。

此外,使用 MessageDrivenContext.getRollbackOnly() 会返回 false。我希望它是真的。

如果我使用TransactionAttributeType.REQUIRES_NEW 执行我的业务方法,则事务将回滚并且消息不会重新传递,但是消息处理事务将是单独的,这也是不希望的。我确实设置了一个 JDBC 存储来将消息保存在数据库中。

我会留下一些虚拟类来说明我的观点。

MDB 消息处理

提取有效负载后,我将其转发到会话 bean 以处理持久性逻辑。

public void onMessage(Message message) {

    try {
        // Extract the payload
        TextMessage txtMsg = (TextMessage) message;
        String employeeName = txtMsg.getText();

        // Call service
        service.createEmployee(employeeName);

    } catch (Exception e) {
        e.printStackTrace();            
    } finally {
        // When the JPA provider rollbacks back the transaction, this value
        // is still "false"
        log.info(String.format("Rollback only: [%s]", mdContext.getRollbackOnly()));
    }
}

对 JPA 提供者强制异常

通过将null 留在非空字段中来强制出错。

// Message and business will run in the same transaction
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public void createEmployee(String name) {

    Employee employee = new Employee();
    employee.setName(null); // Null value to force constraint error

    try {
        // This part triggers the exception within the JPA provider, and the
        // Java EE transaction is rolledback and forces the JMS message to be
        // redelivered.
        em.persist(employee);

    } catch (Exception e) {
        // Capturing the exception does not affect the rollback behavior
        e.printStackTrace();
    }
}

这是 EclipseLink 抛出的错误。它被包裹在RuntimeException 中,因此它是一个系统异常并且事务将回滚。

javax.ejb.EJBTransactionRolledbackException: EJB Exception: ; nested exception is: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.1.v20111018-r10243): org.eclipse.persistence.exceptions.DatabaseException

【问题讨论】:

    标签: java jpa transactions jms weblogic


    【解决方案1】:

    恕我直言,拥有 2 笔交易正是您要找的。

    有一个事务来处理由 EJB 容器启动的 MDB,然后在 MDB 中调用无状态会话 bean (SLSB) 中的一个业务方法,该方法使用 Transaction.REQUIRES_NEW 注释,因此您的业务逻辑处理数据库在其自己的隔离事务中被隔离。

    当您从 SLSB 的方法返回时,无论您的业务逻辑方法中发生了什么,MDB tx 都将由容器在 MDB 的 onMessage() 方法结束时提交,并且消息将被消费/从Q中删除。再加工的可能性为零......

    唯一的例外是如果 MDB 本身出现问题,但您可以轻松处理它

    干杯

    【讨论】:

      【解决方案2】:

      您使用的是什么 Q Provider?

      您似乎已经涵盖了可能性

      恕我直言,最好的解决方案是将使用 JPA 的业务逻辑隔离在具有“Transaction new”属性的单独外观 SLSB 中,并且仍然使用 CMT 语义,因此如果此 SLSB 中发生不好的事情,您仍然可以提交 tx您的应用程序服务器自动启动 onMessage tx 运行的位置

      这听起来与您的第一个解决方案相似。

      对我来说,第二个选择是配置 Q 管理器,因此当消息在 Q 上“重新传递”一定次数(即由于 tx 回滚而放回 Q)时,消息将移动到另一个 Q(死信队列)(并在此 Q 上配置一些监控/警报系统...

      丹尼斯(JMSToolBox 的作者)

      【讨论】:

      • 我正在使用 WLS 11g 附带的那个。您提出的第二个选择也将包含在我的第三个选项中,但在大多数情况下,我会丢弃这些消息。不确定您的第一个选项,我宁愿控制消息的复杂性也不愿冒重新处理业务事件的小风险。
      猜你喜欢
      • 2017-01-30
      • 2016-12-27
      • 1970-01-01
      • 2011-10-27
      • 1970-01-01
      • 2014-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多