如何在Java EE环境中使用JMS和JPA处理事务回滚?



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

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

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

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

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

你们怎么看?

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

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

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

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

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

多边开发银行消息处理

提取有效负载后,我将其转发到会话 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

恕我直言,拥有 2 个 tx 正是您正在寻找的。

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

当你从 SLSB 的方法回来时,无论你的业务逻辑方法中发生了什么,MDB tx 都将由 MDB 的 onMessage() 方法末尾的容器提交,并且消息将被使用/从 Q 中删除。再处理的可能性为零...

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

干杯

您使用的是什么 Q 提供程序?

似乎您已经涵盖了可能性

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

这听起来类似于您的第一个解决方案。

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

Denis(JMSToolBox的作者)

最新更新