3

我正在开发一项服务,在该服务中我侦听队列、反序列化收到的消息并将它们保存到数据库 (Oracle)。大致:

@JmsListener(destination="some-destination")
public void onMessage(Message message) throws Exception {
    String message = ((TextMessage) message).getText();
    service.save(deserialize(message));
    // includes exception handling etc
}

在默认消息侦听器 bean 中,我设置了并发和setSessionTransacted(true). 这足以使整个 onMessage 事务处理吗?以便在一个事务中接收并保存消息,并在任何这些点出现故障时回滚?当尝试保存特定消息时,我尝试在特定消息上抛出异常 - 消息确实回滚到队列中并且侦听器尝试再次使用它们,这是一种理想的行为。在研究这个的时候,我偶然发现了分布式事务,jta事务管理器,但是我仍然不确定除了setSessionTransacted(true)之外是否需要配置更多,或者Spring Boot是否自动处理XA资源的事务。寻求建议。谢谢你。

4

1 回答 1

1

如果您的侦听器正在与接收消息的 JMS 代理以外的事务性资源进行交互,那么调用不足以onMessage()使所有交互成为事务性的。setSessionTransacted(true)

JMS 中的“事务”会话仅涵盖具有该特定会话的 JMS 操作。它不包括使用任何其他事务资源(例如数据库)。

如果您想要一个涉及侦听器对消息的消费以及侦听器完成的任何其他事务工作(例如在数据库中更新或插入记录,将 JMS 消息发送到另一个代理等)的事务,那么您需要一个 JTA事务管理器,它可以使用 XA 资源来协调各种事务阶段(例如准备、提交、回滚)。这些类型的交易有时被称为“分布式”交易。

这是一个相当常见的用例,因为它使 JMS 消息成为一种“工作单元”,并且您知道该消息是否已被使用,那么与该消息相关的所有工作也已成功完成,反之亦然。这是 MDB 在 Java EE 中提供的主要功能之一,但同样的基本工作也可以在 Spring 中完成。

根据Spring Boot 文档,您可以与一些不同的事务管理器(例如 Atomikos、Bitronix、Narayana 等)集成来完成此类工作。

需要明确的是,在某些情况下,您现有的安排将以这样的方式工作,以使这两个操作看起来像是在同一个事务中。例如,如果您的数据库操作引发异常并且该异常是从 引发的,onMessage()则消息将回滚到队列中。但是,在这种情况下,这两个操作只是相关的。它们不在同一个事务中,因此它们实际上不是原子的。相反,如果数据库操作成功,然后由于某种原因从侦听器的onMessage()或者 JMS 代理在事务会话可以提交之前崩溃,那么消息最终将回滚到队列中,但数据仍然在数据库中,因此如果您再次使用该消息,您表面上会将相同的数据写入再次访问数据库。

于 2020-10-03T20:25:42.313 回答