到目前为止,对于事务的探讨基本上都聚焦在本地事务上,本地事务只会波及到一个繁多的数据源。本章开始介绍分布式事务,分布式事务会在单个事务内波及多个数据源。以下内容次要包含:
- 散布识事务基础设施
- 事务管理器和资源管理器
XADataSource
,XAConnection
和XAResource
接口- 两阶段提交
JDBC 的事务管理 API 与 JTA 标准是兼容的。
基础设施
分布式事务的基础设施包含以下几个局部:
- 事务管理器,用来管制事务的边界和治理两阶段提交协定。它也应该是 JTA 的一个经典实现。
- 实现了
XADataSource
,XAConnection
和XAResource
接口的 JDBC 驱动。 - 一个对于应用层齐全可见的
DataSource
实现,利用它来操作XADataSource
,并与事务管理器交互。通常这个实现由应用服务器提供。 - 用来治理底层数据的资源管理器。在 JDBC 的语境中,资源管理器指的是数据库服务器。“资源管理器”这个术语实际上来自于 JTA,在这里应用这个术语是为了强调 JDBC 中的分布式事务是遵循 JTA 标准的架构来解决的。
通常会以“三层架构”来实现这个基础设施,包含:
- 客户端
- 一个蕴含应用程序、EJB 服务器、JDBC 驱动汇合的中间层
- 多个资源管理器
分布式事务也能够实现为“两层架构”。在两层架构中,应用层自身就会表演事务管理器的角色,并且间接操作 XADataSource
API。下图论述了分布式事务的基础设施:
后续的内容将会对基础设施的各个局部进行具体的阐明
XADataSource
和 XAConnection
XADataSource
和 XAConnection
接口,定义在 javax.sql 包中。反对分布式事务的数据库驱动须要实现这两个接口。XAConnection
继承了 PooledConnection
接口,增加了一个 getXAResource
办法,这个办法会生成一个 XAResource
对象,事务管理器能够利用这个对象来实现分布式事务。以下是 XAConnection
接口的定义:
public interface XAConnection extends PooledConnection {javax.transaction.xa.XAResource getXAResource()
throws SQLException;
}
因为继承了 PooledConnection
接口,所以所有的 XAConnection
对象也反对 PooledConnection
中定义的办法。这些对象代表着与底层数据源的一条可重用的物理连贯,应用层也能够通过这个对象操作这条连贯。
XAConnection
对象由 XADataSource
生成。ConnectionPoolDataSource
和 XADataSource
有一些类似的中央,他们都实现了 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 驱动应保护 XAResource
与 XAConnection
的一对一关系,也就是说对于 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
对象实现二阶段提交协定。这些步骤是基于应用具备内部事务管理器的应用服务器的“三层架构”实现的。
-
应用服务器从两个不同的连贯中获取
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();
- 应用服务器传递
XAResource
对象给事务管理器,事务管理器不间接与XAConnection
对象交互。 -
事务管理器利用
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);
-
事务管理器启动两阶段提交协定,申请两个参与者进行投票:
resourceA.prepare(xid); resourceB.prepare(xid);
如果一个事务参与者想要投票 rollback,它须要抛出一个 javax.transaction.xa.XAException
。
-
如果两个事务参与者都投票 commit,事务参与者告诉两个参与者提交他们的事务分支:
resourceA.commit(xid, false); resourceB.commit(xid, false);
-
如果任何一个事务参与者投票 rollback,事务管理器则告诉各个参与者回滚它们的事务分支:
resourceA.rollback(xid); resourceB.rollback(xid);
事务管理器在解决一个事务分支的不同阶段的时候,不肯定要用的是同一个 XAResource
对象,只有这两个对象的连贯是来自于同一个事务管理器。
连贯敞开
当在分布式事务环境中,应用层应用完一条连贯后,中间层服务器应该失去告诉。在先前对 PooledConnection
对象的探讨中,咱们指出了当 Connection.close
办法被调用时,中间件服务器会作为一个 ConnectionEventListener
失去告诉。在这时,事务管理器也会失去告诉,并且完结对应的事务分支。如果 DataSource
蕴含池化模块,那么池化模块也必须失去告诉,以便将 XAConnection
偿还。
留神,即便一个连贯被敞开,分布式事务也仍然能够处于沉闷状态。
XAResource 接口的局限性
XAResource
接口只定义了 X/Open XA 标准中要求定义的办法。如果一个资源管理器反对了一些在 XA 标准中没有定义的个性,那么应用层无奈显著地通过 API 去应用这些个性,只能交给具体的驱动在底层去解决。如果利用间接利用了这个个性,这又会带来一个可移植性的问题。