Skip to content

分布式事务规范 JTA

JTA 概述

JTA 全称 Java Transaction API,是 X/OPEN CAE 规范中分布式事务 XA 规范在 Java 中的映射, 是 Java 中使用事务的标准 API,同时支持单机事务与分布式事务。

作为 J2EE 平台规范的一部分,JTA 与 JDBC 类似,自身只提供了一组 Java 接口,需要由供应商来实现这些接口

JTA 定义了分布式事务的 5 种角色,不同角色关注不同的内容,如下图所示。

gradle
  // api 坐标 
  implementation 'jakarta.transaction:jakarta.transaction-api:2.0.1'
xml
<dependency>
    <groupId>jakarta.transaction</groupId>
    <artifactId>jakarta.transaction-api</artifactId>
    <version>2.0.1</version>
</dependency>

1. 事务管理器

事务管理器 Transaction Manager 是分布式事务的核心,在 JTA 中使用 TransactionManager 接口表示, 提供了事务操作、资源管理、同步、事务传播等功能,等同于 Spring 中的 PlatformTransactionManager。org.springframework.transaction.jta.JtaTransactionManager 是其jta实现

  • 事务管理:包括将事务对象存至线程上下文,开始事务,提交事务,回滚事务等常规操作。
  • 资源管理:将资源与事务对象建立关系,以便通过两阶段提交实现事务。
  • 同步:同步原文为 Synchronization,其实是事务完成前后的一个回调接口。
  • 事务传播:指在一个事务中开启子事务时,子事务的行为,与 Spring 事务传播行为类似。

2. 资源管理器

资源管理器 Resource Manager 提供了对资源访问的能力,典型的代表是关系型数据库、mq消息队列 ,在 JTA 中使用接口 javax.transaction.xa.XAResource 表示,通常通过一个资源适配器来实现,例如 JDBC 中的数据库驱动。

提示

XAResource 接口是基于 X/Open CAE 规范(分布式事务处理:XA 规范)的工业标准 XA 接口的 Java 映射。 接口是 jdk 自带 java.transaction.xa包 参考:https://tool.oschina.net/uploads/apidocs/jdk-zh/javax/transaction/xa/XAResource.html

3. 通信资源管理器

通信资源管理器用于支持跨应用分布式事务的事务管理器之间的通信,JTA 规定它实现 JTS 规范定义的接口即可,通常用户不用关心。

4. 应用

使用分布式事务的应用,应用可以通过 JTA 规范中的 UserTransaction 接口来操作事务。spring jta 中提供 org.springframework.transaction.jta.UserTransactionAdapter适配器

5. 应用服务器

应用的运行环境,JTA 规定事务管理器应该由应用服务器来实现,如 jboss、weblogic、websphere, 不过并非所有的应用服务器都实现了事务管理器,如 Tomcat。如果想在标准环境使用 JTA,可以使用支持 JTA 的第三发类库,如 Atomikos、Bitronix。

提示

Seata 提供了两阶段提交,但是并没有实现 jta 接口 参考:https://seata.apache.org/zh-cn/

两阶段提交

事务中有一个特性是原子性,它表示事务中的所有操作要么全部完成,要么全部不完成。事务管理器通过两阶段提交实现这一特性。

两阶段提交将对事务的提交分成两部分,分别为准备阶段和提交阶段。

在准备阶段,事务管理器向资源管理器(如 JMS 消息队列或数据库)询问是否同意提交事务,如果资源管理器回复同意,则表示资源管理器可以将资源持久化。

在提交阶段,如果所有的资源管理器都回复同意,则事务管理器向所有的资源管理器发出提交请求,否则事务管理器向资源管理器发出回滚请求。

正常情况下的两阶段提交可以用如下的图来表示。

null

务回滚的两阶段提交过程可以用如下图来表示。

null

想到一个问题,虽然准备阶段事务管理器与资源管理器正常交互,不过在提交阶段如果发生一些意外导致事务管理器与资源管理器之间的通信中断怎么办呢?这很可能导致分布式事务违反原子性。

  • 如果资源管理器已经接收到事务管理器的提交请求,事务管理器恢复后要求资源管理器回滚将抛出 HeuristicCommitException 异常
  • 如果资源管理器决定回滚,事务管理器恢复后要求资源管理器提交,将抛出 HeuristicRollbackException 异常。
  • 如果一部分资源管理器将资源提交,另一部分资源管理器将资源回滚,将导致 HeuristicMixedException 异常。

异常的场景可以用如下图来描述。

null

JTA API

上面只是介绍了 JTA 相关的一些概念和基本原理,要想理解 JTA,还得看相关 API

JTA API 分类 JTA 的 API 大概可以分别为三部分,如下。

null

UserTransaction 也同样需要事务管理器来实现。

JTA 相关 API 除了 JTA 规范中的 API,特定于实现的资源管理器通常还会实现其他与 JTA 相关的接口。

    1. JDBC 对于关系型数据库来说,还需要实现接口 javax.sql.XADataSource 与 javax.sql.XAConnection。

XADataSource 接口定义如下。

java
public interface XADataSource extends CommonDataSource {

  XAConnection getXAConnection() throws SQLException;

  XAConnection getXAConnection(String user, String password) throws SQLException;
}

XAConnection 接口定义如下。

java
public interface XAConnection extends PooledConnection {

  javax.transaction.xa.XAResource getXAResource() throws SQLException;

}

也就是说,通过 XADataSource 获取 XAConnection,通过 XAConnection 获取 XAResource,然后再进行资源的相关操作。例如,MySQL 的 JDBC 驱动包就进行了相关实现。

java
package com.mysql.cj.jdbc;
...
public class MysqlXAConnection extends MysqlPooledConnection implements XAConnection, XAResource {
...
}
    1. JMS

对于 JMS 规范来说,消息队列作为资源管理器,除了要实现 XAResource 接口,还要实现 javax.jms.XASession 与 javax.jms.XAConnection 接口。这两个接口定义如下。

xml
public interface XAConnection extends Connection {
    XASession createXASession() throws JMSException;

    Session createSession(boolean var1, int var2) throws JMSException;
}

public interface XASession extends Session {
    Session getSession() throws JMSException;

    XAResource getXAResource();

    boolean getTransacted() throws JMSException;

    void commit() throws JMSException;

    void rollback() throws JMSException;
}

与 JDBC 类似,JMS 通过 XAConnection 获取 XASession,通过 XASession 获取 XAResource,然后再进行资源相关操作。

TransactionManager

事务管理器可以创建新的事务,并设置事务的相关属性,还允许应用获取创建后的事务,并且将事务与线程绑定。具体有以下方法。

  • begin:创建新的事务,并且将事务与线程关联,如果不支持嵌套事务并且事务已存在将抛出 NotSupportedException 异常。
  • commit:提交与线程关联的事务,并断开线程与事务的关联。如果没有事务与线程关联将抛出异常。实现将触发 Transaction.commit、XAResource.prepare、XAResource.commit 方法调用。
  • rollback:回滚线程关联的事务,并断开线程与事务的关联。如果没有事务与线程关联也将抛出异常。实现将触发 Transaction.rollback、XAResource.rollback 方法的调用。
  • getTransaction:获取线程关联的事务 Transaction 对象,如果没有事务与当前线程关联则返回 null。
  • setTansactionalTimeout:设置线程关联事务的超时秒数。
  • getStatus:获取线程关联事务的状态。
  • setRollbackOnly:设置线程关联事务的唯一结果是回滚。
  • suspend:暂时挂起与线程关联的事务,将断开线程与事务的关联,方法返回的 Transaction 对象可作为 resume 方法参数恢复线程与事务的关系。实现将触发 Transaction.delistResource 与 XResource.end 方法的调用。
  • resume:恢复指定事务与线程之间的关联。实现将触发 Transaction.enlistResource、XAResource.start 方法的调用。

Transaction

Transaction 事务接口用于对活动的事务进行操作,这里活动的事务是指未提交的事务,对其他事务不可见。

  • enlistResource:将资源添加到事务,以便执行两阶段提交,允许添加多个资源,资源的最终的操作结果将与事务保持一致,即要么提交、要么回滚。实现将触发 XAResource.start 方开启事务分支。
  • delistResource:解除资源与事务的关联。实现将触发 XAResource.end 方法调用结束事务分支。
  • registerSynchronization:注册 Synchronization,接口方法将在事务完成前后被回调。
  • commit:与 TransactionManager.commit 含义相同。
  • getStatus:与 TransactionManager.getStatus 含义相同。
  • rollback:与 TransactionManager.rollback 含义相同。
  • setRollbackOnly:与 TransactionManager.setRollbackOnly 含义相同。

Xid

表示事务与资源的关联,一个事务可以关联多个资源。可以由资源管理器或事务管理器来实现这个接口。接口方法如下:

  • getFormatId:获取全局事务格式 ID。
  • getGlobalTransactionId:获取全局事务ID。
  • getBranchQualifier:获取事务分支限定符。

XAResource

XAResource 用来表示分布式事务角色中的资源管理器,资源适配器将实现 XAResource 接口以支持事务与资源的关联,允许不同的线程同时对 XAResource 进行操作,但一个 XAResource 同时只能关联一个事务。事务提交时,触发资源管理器准备与提交。具体方法如下:

  • start:开启事务分支,全局事务通过该方法与事务资源关联,由资源适配器获取资源(连接)时隐式触发。一但调用该方法资源将一直保持开启状态,直到释放(close)资源。
  • end:结束事务分支,解除资源与事务的关联,调用该方法后可再调用 start 方法与其他事务建立关联。
  • prepare:为 xid 指定的事务提交做准备。
  • commit:提交 xid 指定的事务分支。
  • rollback:回滚 xid 指定的事务分支。
  • isSameRM:判断当前资源管理器是否与给定的资源管理器相同,用于 Transaction.enlistResource 添加资源时判断资源是否已添加。
  • recover:用于意外导致事务管理器与资源管理器断开通信后,事务管理器恢复时查询准备好的事务。XAResource 在故障发生时未持久化,事务管理器需要有某种方法查找 XAResource,如通过 JNDI 查找机制,事务管理器可以忽略不属于它的事务。
  • forget:用于忘记准备好的事务分支。
  • getTransactionTimeout:获取事务超时秒数。
  • setTransactionTimeout:设置事务超时秒数。

Synchronization

这个接口比较简单,用来表示事务完成前后的回调,由应用实现这个接口。这个接口未持久化,崩溃恢复事务后将丢失 Synchronization 实例。接口方法如下:

  • beforeCompletion:事务提交前回调该方法。
  • afterCompletion:事务提交或或回滚后调用该方法

UserTransaction

功能受限的事务接口,暴露给应用使用,由事务管理器实现。

方法包括 begin、commit、rollback、setRollbackOnly、getStatus、setTransactionTimeout,含义与 Transaction 中的方法相同,不再赘述。

JTA 活动事务交互

前面已经介绍了事务提交、回滚、异常场景下各组件的交互,事务提交之前的活动事务也有自己的交互流程。根据前面 API 的介绍,可以大概总结流程如下。

null

close 才会调用 Transaction.delistResource 释放资源,这意味着应该在 try{}finaly{} 中的 finally 块关闭连接。

版权声明