乐趣区

关于jdbc:JDBC-42-Specifications-中文翻译-第十二章-分布式事务

到目前为止,对于事务的探讨基本上都聚焦在本地事务上,本地事务只会波及到一个繁多的数据源。本章开始介绍分布式事务,分布式事务会在单个事务内波及多个数据源。以下内容次要包含:

  • 散布识事务基础设施
  • 事务管理器和资源管理器
  • XADataSourceXAConnectionXAResource 接口
  • 两阶段提交

JDBC 的事务管理 API 与 JTA 标准是兼容的。

基础设施

分布式事务的基础设施包含以下几个局部:

  • 事务管理器,用来管制事务的边界和治理两阶段提交协定。它也应该是 JTA 的一个经典实现。
  • 实现了 XADataSourceXAConnectionXAResource 接口的 JDBC 驱动。
  • 一个对于应用层齐全可见的 DataSource 实现,利用它来操作 XADataSource,并与事务管理器交互。通常这个实现由应用服务器提供。
  • 用来治理底层数据的资源管理器。在 JDBC 的语境中,资源管理器指的是数据库服务器。“资源管理器”这个术语实际上来自于 JTA,在这里应用这个术语是为了强调 JDBC 中的分布式事务是遵循 JTA 标准的架构来解决的。

通常会以“三层架构”来实现这个基础设施,包含:

  1. 客户端
  2. 一个蕴含应用程序、EJB 服务器、JDBC 驱动汇合的中间层
  3. 多个资源管理器

分布式事务也能够实现为“两层架构”。在两层架构中,应用层自身就会表演事务管理器的角色,并且间接操作 XADataSource API。下图论述了分布式事务的基础设施:

后续的内容将会对基础设施的各个局部进行具体的阐明

XADataSourceXAConnection

XADataSourceXAConnection 接口,定义在 javax.sql 包中。反对分布式事务的数据库驱动须要实现这两个接口。XAConnection 继承了 PooledConnection 接口,增加了一个 getXAResource 办法,这个办法会生成一个 XAResource 对象,事务管理器能够利用这个对象来实现分布式事务。以下是 XAConnection 接口的定义:

public interface XAConnection extends PooledConnection {javax.transaction.xa.XAResource getXAResource() 
throws SQLException;
}

因为继承了 PooledConnection 接口,所以所有的 XAConnection 对象也反对 PooledConnection 中定义的办法。这些对象代表着与底层数据源的一条可重用的物理连贯,应用层也能够通过这个对象操作这条连贯。

XAConnection 对象由 XADataSource 生成。ConnectionPoolDataSourceXADataSource 有一些类似的中央,他们都实现了 DataSource 接口。这样就容许分布式事务的底层实现对于应用层来说是通明的。XADataSource 接口定义如下:

public interface XADataSource {XAConnection getXAConnection() throws SQLException;
XAConnection getXAConnection(String user, 
String password) throws SQLException;
//...
}

通常,一个基于 XADataSource 之上的 DataSource 实现,也会蕴含一个连接池化的模块。

部署 XADataSource 对象

部署一个 XADataSource 与先前所提到的 ConnectionPoolDataSource 是一样的。流程总共分两步,如下代码所示:

// com.acme.jdbc.XADataSource implements the 
// XADataSource interface.
// Create an instance and set properties.
com.acme.jdbc.XADataSource xads = new com.acme.jdbc.XADataSource();
xads.setServerName(“bookstore”);
xads.setDatabaseName(“bookinventory”);
xads.setPortNumber(9040);
xads.setDescription(“XADataSource for inventory”);
// First register xads with a JNDI naming service, using the
// logical name“jdbc/xa/inventory_xa”Context ctx = new InitialContext();
ctx.bind(“jdbc/xa/inventory_xa”, xads);
// Next register the overlying DataSource object for application
// access. com.acme.appserver.DataSource is an implementation of
// the DataSource interface.
// Create an instance and set properties. 
com.acme.appserver.DataSource ds = 
new com.acme.appserver.DataSource();
ds.setDescription(“Datasource supporting distributed transactions”);
// Reference the previously registered XADataSource
ds.setDataSourceName(“jdbc/xa/inventory_xa”);

// Register the DataSource implementation with a JNDI naming service,
// using the logical name“jdbc/inventory”.
ctx.bind(“jdbc/inventory”, ds);

获取连贯

调用 DataSource.getConnection 办法返回一个底层实现为 XAConnection 的逻辑 Connection 对象:

Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup(“jdbc/inventory”);
Connection con = ds.getConnection(“myID”,“mypasswd”);

DataSource.getConnection 办法的底层实现如下所示:

// Assume xads is a driver’s implementation of XADataSource
XADataSource xads = (XADataSource)ctx.lookup(“jdbc/xa/"+"inventory_xa”);
// xacon implements XAConnection
XAConnection xacon = xads.getXAConnection(“myID”,“mypasswd”);
// Get a logical connection to pass back up to the application
Connection con = xacon.getConnection();
CODE EXAMPLE 12-5 Getting a logical connection from an

XAResource

XAResource 接口是 JTA 标准中的一个定义,也是 Java 语言中对应 X/Open 组织的 XA 的对等概念。XAResource 对象通过调用 XAConnection.getXAResource 办法取得,通过调用该形式将 XAConnection 与一个分布式事务绑定。同一时刻,一个 XAConnection 只能与一个分布式事务绑定。JDBC 驱动应保护 XAResourceXAConnection 的一对一关系,也就是说对于 getXAResource 办法的屡次调用,都返回同一个 XAResource 对象。

通常,两头应用服务器调用 XAConnection.getXAResource 办法失去一个 XAResource 对象后,会将它交给内部的事务管理器,事务管理器并不需要间接与 XAConnection 交互,它间接利用 XAResource 对象。

事务管理器治理多个 XAResource 对象,每一个都代表一个参加到分布式事务的资源管理器,留神,不同的 XAResource 对象可能指向同一个资源管理器,当不同的事务参与者应用同一个 XADataSource,就有可能呈现这种状况。

XAResource 定义了以下的办法,来实现两阶段提交协定,每个办法都要求有一个 xid 参数,用来标识一个分布式事务。

  • start 办法。告诉资源管理器后续的操作来处于一个分布式事务中。
  • end 办法。告诉资源管理器事务完结。
  • prepare 办法。获取资源管理器对于事务应该回滚还是提交的投票。
  • commit 办法。告诉资源管理器提交它的事务分支。只有当所有的参与者都投票提交全局事务时,这个办法能力被调用。
  • rollback 办法。告诉资源管理器回滚它的事务分支。只有当至多一个事务参与者投票回滚全局事务时,这个办法才会被调用。

JTA 标准中有对 XAResource 残缺的形容。

事务管理

通过 XAResource.start 和 XAResource.end 办法来定义事务边界。边界内事务模式为全局事务。边界外的事务为本地事务。

除了一些束缚,一个事务参与者的利用代码应该怎么写,与它是否参加分布式事务没有什么关系。分布式事务的边界个别都是由内部的事务管理者来定义的,事务的参与者不能调用 Connection 类的某些办法:

  • setAutoCommit(true)
  • commit
  • rollback
  • setSavepoint

如果事务参与者试图调用这些办法,JDBC 驱动应抛出 SQLException。当分布式事务完结后,这些办法的调用就不再有限度,并且利用于本地事务。

在事务边界内,事务参与者应该防止调用 Connection.setTransactionIsolation 办法,这个办法的行为不做束缚,由驱动自主决定。

如果一个连贯在参加分布式事务前的 autocommit 属性曾经为 true,那么当它参加分布式事务时,这个属性会被疏忽,当事务完结后,属性才从新失效。

两阶段提交

以下几个步骤论述了事务管理器如何利用 XAResource 对象实现二阶段提交协定。这些步骤是基于应用具备内部事务管理器的应用服务器的“三层架构”实现的。

  1. 应用服务器从两个不同的连贯中获取 XAResource 对象

    // XAConA connects to resource manager A
    javax.transaction.xa.XAResource resourceA = XAConA.getXAResource();
    // XAConB connects to resource manager B 
    javax.transaction.xa.XAResource resourceB = XAConB.getXAResource();
  2. 应用服务器传递 XAResource 对象给事务管理器,事务管理器不间接与 XAConnection 对象交互。
  3. 事务管理器利用 XAResource 将资源管理器纳入事务中,整个事务以一个 xid 作为标识,由事务管理器在启动事务时负责生成。

    // Send work to resource manager A. The TMNOFLAGS argument indicates
    // we are starting a new branch of the transaction, not joining or 
    // resuming an existing branch. 
    resourceA.start(xid, javax.transaction.xa.TMNOFLAGS);
    // do work with resource manager A
    ...
    // tell resource manager A that it’s done, and no errors have occurred 
    resourceA.end(xid, javax.transaction.xa.TMSUCCESS);
    // do work with resource manager B.
    resourceB.start(xid, javax.transaction.xa.TMNOFLAGS);
    // B’s part of the distributed transaction
    ...
    resourceB.end(xid, javax.transaction.xa.TMSUCCESS);
  4. 事务管理器启动两阶段提交协定,申请两个参与者进行投票:

    resourceA.prepare(xid);
    resourceB.prepare(xid);

如果一个事务参与者想要投票 rollback,它须要抛出一个 javax.transaction.xa.XAException

  1. 如果两个事务参与者都投票 commit,事务参与者告诉两个参与者提交他们的事务分支:

    resourceA.commit(xid, false);
    resourceB.commit(xid, false);
  2. 如果任何一个事务参与者投票 rollback,事务管理器则告诉各个参与者回滚它们的事务分支:

    resourceA.rollback(xid);
    resourceB.rollback(xid);

事务管理器在解决一个事务分支的不同阶段的时候,不肯定要用的是同一个 XAResource 对象,只有这两个对象的连贯是来自于同一个事务管理器。

连贯敞开

当在分布式事务环境中,应用层应用完一条连贯后,中间层服务器应该失去告诉。在先前对 PooledConnection 对象的探讨中,咱们指出了当 Connection.close 办法被调用时,中间件服务器会作为一个 ConnectionEventListener 失去告诉。在这时,事务管理器也会失去告诉,并且完结对应的事务分支。如果 DataSource 蕴含池化模块,那么池化模块也必须失去告诉,以便将 XAConnection 偿还。

留神,即便一个连贯被敞开,分布式事务也仍然能够处于沉闷状态。

XAResource 接口的局限性

XAResource 接口只定义了 X/Open XA 标准中要求定义的办法。如果一个资源管理器反对了一些在 XA 标准中没有定义的个性,那么应用层无奈显著地通过 API 去应用这些个性,只能交给具体的驱动在底层去解决。如果利用间接利用了这个个性,这又会带来一个可移植性的问题。

退出移动版