38、JTA 分布式事务

Spring Boot 通过使用 AtomikosBitronix 嵌入式事务管理器来支持跨多个 XA 资源的分布式 JTA 事务。部署在某些 Java EE 应用服务器(Application Server)上也支持 JTA 事务。

当检测到 JTA 环境时,Spring 的 JtaTransactionManager 将用于管理事务。自动配置的 JMS、DataSource 和 JPA bean 已升级为支持 XA 事务。您可以使用标准的 Spring 方式(例如 @Transactional)来使用分布式事务。如果您处于 JTA 环境中并且仍想使用本地事务,则可以将 spring.jta.enabled 属性设置为 false 以禁用 JTA 自动配置。

38.1、使用 Atomikos 事务管理器

Atomikos 是一个流行的开源事务管理器,可以嵌入到 Spring Boot 应用程序中。您可以使用 spring-boot-starter-jta-atomikos starter 来获取相应的 Atomikos 库。Spring Boot 自动配置 Atomikos 并确保将合适的依赖设置应用于 Spring bean,以确保启动和关闭顺序正确。

默认情况下,Atomikos 事务日志将写入应用程序主目录(应用程序 jar 文件所在的目录)中的 transaction-logs 目录。您可以通过在 application.properties 文件中设置 spring.jta.log-dir 属性来自定义此目录的位置。也可用 spring.jta.atomikos.properties 开头的属性来自定义 Atomikos UserTransactionServiceImp。有关完整的详细信息,请参阅 AtomikosProperties Javadoc

注意

为确保多个事务管理器可以安全地协调相同的资源管理器,必须为每个 Atomikos 实例配置唯一 ID。默认情况下,此 ID 是运行 Atomikos 的计算机的 IP 地址。在生产环境中要确保唯一性,应为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,并使用不同的值。

38.2、使用 Bitronix 事务管理器

Bitronix 是一个流行的开源 JTA 事务管理器实现。您可以使用 spring-boot-starter-jta-bitronix starter 为您的项目添加合适的 Bitronix 依赖。与 Atomikos 一样,Spring Boot 会自动配置 Bitronix 并对 bean 进行后处理(post-processes),以确保启动和关闭顺序正确。

默认情况下,Bitronix 事务日志文件(part1.btmpart2.btm)将写入应用程序主目录中的 transaction-logs 目录。您可以通过设置 spring.jta.log-dir 属性来自定义此目录的位置。以 spring.jta.bitronix.properties 开头的属性绑定到了 bitronix.tm.Configuration bean,允许完全自定义。有关详细信息,请参阅 Bitronix 文档

注意

为确保多个事务管理器能够安全地协调相同的资源管理器,必须为每个 Bitronix 实例配置唯一的 ID。默认情况下,此 ID 是运行 Bitronix 的计算机的 IP 地址。生产环境要确保唯一性,应为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,并使用不同的值。

38.3、使用 Java EE 管理的事务管理器

如果将 Spring Boot 应用程序打包为 warear 文件并将其部署到 Java EE 应用程序服务器,则可以使用应用程序服务器的内置事务管理器。Spring Boot 尝试通过查找常见的 JNDI 位置(java:comp/UserTransactionjava:comp/TransactionManager 等)来自动配置事务管理器。如果使用应用程序服务器提供的事务服务,通常还需要确保所有资源都由服务器管理并通过 JNDI 暴露。Spring Boot 尝试通过在 JNDI 路径(java:/JmsXAjava:/JmsXA)中查找 ConnectionFactory 来自动配置 JMS,并且可以使用 spring.datasource.jndi-name 属性来配置 DataSource

38.4、混合使用 XA 与非 XA JMS 连接

使用 JTA 时,主 JMS ConnectionFactory bean 可识别 XA 并参与分布式事务。在某些情况下,您可能希望使用非 XA ConnectionFactory 处理某些 JMS 消息。例如,您的 JMS 处理逻辑可能需要比 XA 超时时间更长的时间。

如果要使用非 XA ConnectionFactory,可以注入 nonXaJmsConnectionFactory bean 而不是 @Primary jmsConnectionFactory bean。为了保持一致性,提供的 jmsConnectionFactory bean 还需要使用 xaJmsConnectionFactory 别名。

以下示例展示了如何注入 ConnectionFactory 实例:

  1. // Inject the primary (XA aware) ConnectionFactory
  2. @Autowired
  3. private ConnectionFactory defaultConnectionFactory;
  4. // Inject the XA aware ConnectionFactory (uses the alias and injects the same as above)
  5. @Autowired
  6. @Qualifier("xaJmsConnectionFactory")
  7. private ConnectionFactory xaConnectionFactory;
  8. // Inject the non-XA aware ConnectionFactory
  9. @Autowired
  10. @Qualifier("nonXaJmsConnectionFactory")
  11. private ConnectionFactory nonXaConnectionFactory;

38.5、支持嵌入式事务管理器

XAConnectionFactoryWrapperXADataSourceWrapper 接口可用于支持其他嵌入式事务管理器。接口负责包装 XAConnectionFactoryXADataSource bean,并将它们公开为普通的 ConnectionFactoryDataSource bean,它们透明地加入分布式事务。DataSource 和 JMS 自动配置使用 JTA 变体,前提是您需要有一个 JtaTransactionManager bean 和在 ApplicationContext 中注册有的相应 XA 包装器(wrapper) bean。

BitronixXAConnectionFactoryWrapperBitronixXADataSourceWrapper 为如何编写 XA 包装器提供了很好示例。