引言
JTA(
Java
Transaction API)允许应用
程序
执行分布式事务处理--在两个或多个
网络
计算机资源上访问并且更新数据。JDBC
驱动
程序的JTA支持极大地增强了数据访问能力。
本文的目的是要提供一个关于的Java事务处理API(JTA)的高级的概述,以及与分布式事务相关的内容。一个事务处理定义了一个工作逻辑单元,要么
彻底成功要么不产生任何结果。 一个分布式事务处理只是一个在两个或更多网络资源上访问和更新数据的事务处理,因此它在那些资源之间必然是等价的。在本文
中,我们主要关心的是如何处理关系数据库系统。
我们要讨论的分布式事务处理(DTP)
模型
中包含的组件是:
应用程序
应用程序服务器
事务管理程序
资源适配器
资源管理程序
在以后的内容中,我们将描述这些组件以及它们与JTA和数据库访问的关系。
访问数据库
最好把分布式事务处理中包含的组件看作是独立的过程,而不是考虑它们在一个特定的
电脑
中的位置。这些组件中的一些可以保存在单机中,或者也可在好几台机器之间分布。 下面例子中的图表可以显示在一台特定的电脑上的组件,但是这些操作之间的关系是必须首要考虑的。
最简单的例子:用于本地数据库事务处理的应用程序
关系数据库访问的最简单的形式仅仅包括应用程序、资源管理程序和资源适配器。应用程序只不过是发送请求到数据库并且从数据库中获取数据的最终用户访问点
我们讨论的资源管理程序是一个关系数据库管理系统(RDBMS),比如Oracle或者SQL Server。所有的实际数据库管理都是由这个组件处理的。
资源适配器是外部空间之间的通信管道组件,或者是请求翻译器,在本例中,是应用程序和资源管理程序。在我们的讨论中,这是一个JDBC驱动程序。
下面的描述是资源管理程序本地事务处理的一个描述,也就是说,一个事务处理被被限制在一个特定的企业数据库。
应用程序发送一个用于JDBC驱动程序数据的请求,然后翻译这个请求并把它通过网络发送到数据库中。 数据库把数据发送回驱动程序,然后把翻译的结果发送回应用程序,如下图所示:
这个例子说明了在一个简化的系统中的基本的信息流;然而,今天的企业使用的应用程序服务器都添加了其他的组件到这个过程处理中。
应用程序服务器
应用程序服务器是事务处理操作的另一个组件。应用程序服务器处理大部分的应用程序操作并且获得最终用户应用程序的一些负载。基于前面的例子,我们可以看出应用程序服务器在事务处理上添加了另一个操作层:
到目前为止,我们的例子说明了单个的本地事务处理,并且描述了分布式事务处理
模型
的五个组件中的四个。第五个组件,事务管理程序只有当事务将要被分配的时候才会开始被考虑。
分布式事务处理和事务管理程序
像我们前面所提到的,一个分布式事务处理是一个在两个或更多
网络
资源上访问和更新数据的事务处理。
这些资源可以由好几个位于一个单独服务器上的不同的关系型数据库管理系统组成,比如说Oracle、SQL Server和Sybase;它们也可以包
含存在于若干不同的服务器上的同一种数据库的若干个实例。在任何情况下,一个分布式事务处理包括各种的资源管理程序之间的协同作用。这个协同作用是事务管
理函数。
事务管理程序负责作出要么提交(commit)要么退回(rollback)任何分布式事务处理的决定。一个提交决定应该导致一个成功的事务处理;而退回操作则是保持数据库中的数据不变。 JTA指定一个分布式事务处理中的事务管理程序和另一个组件之间的标准
Java
接口:应用程序,应用程序服务器和资源管理程序。 这个关系被显示在下面的图表中:
在事务管理程序周围的数字框框相应于JTA的三个接口部分:
1—UserTransaction—javax.transaction.UserTransaction接口提供能够编程地控制事务处理范围的应用
程序。 javax.transaction.UserTransaction方法开启一个全局事务并且使用调用线程与事务处理关联。
2—Transaction Manager—javax.transaction.TransactionManager接口允许应用程序服务器来控制代表正在管理的应用程序的事务范围。
3—XAResource—javax.transaction.xa.XAResource接口是一个基于X/Open CAE Specification的行业标准XA接口的Java映射。
注意,一个限制性环节是通过JDBC
驱动
程序的XAResource接口的支持。JDBC驱动程序必须支持两个正常的JDBC交互作用:应用程序和/或应用程序服务器,而且以及JTA的XAResource部分。
编写应用程序水平代码的开发者不会关心分布式事务处理管理的细节。 这是分布式事务处理基本结构的工作—应用程序服务器、事务管理程序和JDBC驱动程
序。应用程序代码中唯一的需要注意的就是当连接处于一个分布式事务范围内的时候,不应该调用一个会影响事务边界的方法。特别的是,一个应用程序不应该调用
Connection方法commit、rollback和setAutoCommit(true),因为它们将破坏分布式事务的基本结构管理。
分布式事务处理
事务管理程序是分布式事务基本结构的基本组件;然而JDBC驱动程序和应用程序服务器组件应该具备下面的特征:
驱动程序应该实现JDBC 2.0应用程序接口,包括Optional Package接口XADataSource和XAConnection以及JTA接口XAResource。
应用程序服务器应该提供一个DataSource类,用来实现与分布式事务基本结的交互以及一个连接池模块(用于改善性能)。
分布式事务处理的第一步就是应用程序要发送一个事务请求到事务管理程序。虽然最后的commit/rollback决定把事务作为一个简单的逻辑单元来
对待,但是仍然可能会包括许多事务分支。一个事务分支与一个到包含在分布式事务中的每个资源管理程序相关联。因此,到三个不同的关系数据库管理的请求需要
三个事务分支。每个事务分支必须由本地资源管理程序提交或者返回。事务管理程序控制事务的边界,并且负责最后决定应该提交或者返回的全部事务。 这个决定
由两个步骤组成,称为Two - Phase Commit Protocol。
在第一步骤中,事务管理程序轮询所有包含在分布式事务中的资源管理程序(关系数据库管理)来看看哪个可以准备提交。如果一个资源管理程序不能提交,它将不响应,并且把事务的特定部分返回,以便数据不被修改。
在第二步骤中,事务管理程序判断否定响应的资源管理程序中是否有能够返回整个事务的。如果没有否定响应的话,翻译管理程序提交整个事务并且返回结果到应用程序中。
开发事项管理程序代码的开发者必须与所有三个JTA接口有关:UserTransaction、TransactionManager和XAResource,这三个接口都被描述在
Sun JTA specification中。JDBC驱动程序开发者只需要关心XAResource接口。这个接口是允许一个资源管理程序参与事务
的行业标准X/Open XA协议的Java映射。连接XAResource接口的驱动程序组件负责在事务管理程序和资源管理程序之间担任"翻译"的任
务。下面的章节提供了XAResource调用的例子。
JDBC驱动
程序
和XAResource
为了简化XAResource的说明,这些例子说明了一个应用程序在不包含应用程序服务器和事项管理程序的情况下应该如何使用JTA。 基本上,这些例
子中的应用程序也担任应用程序服务器和事项管理程序的任务。大部分的企业使用事务管理程序和应用程序服务器,因为它们能够比一个应用程序更能够高效地管理
分布式事务。 然而遵循这些例子,一个应用程序开发者可以测试在JDBC
驱动
程序中的JTA支持的健壮性。而且有一些例子可能不是工作在某个特定的数据库上,这是因为关联在数据库上的一些内在的问题。
在使用JTA之前,你必须首先实现一个Xid类用来标识事务(在普通情况下这将由事务管理程序来处理)。Xid包含三个元素:formatID、gtrid(全局事务标识符)和bqual(分支修饰词标识符)。
formatID通常是零,这意味着你将使用
OSI CCR(Open Systems Interconnection Commitment, Concurrency和Recovery 标
准)来命名。如果你要是用另外一种格式,那么formatID应该大于零。-1值意味着Xid为无效。
gtrid和bqual可以包含64个字节二进制码来分别标识全局事务和分支事务。唯一的要求是gtrid和bqual必须是全局唯一的。此外,这可以通过使用指定在OSI CCR中的命名规则规范来完成。
下面的例子说明Xid的实现:
import javax.transaction.xa.*;
public class MyXid implements Xid
{
protected int formatId;
protected byte gtrid[];
protected byte bqual[];
public MyXid()
{
}
public MyXid(int formatId, byte gtrid[], byte bqual[])
{
this.formatId = formatId;
this.gtrid = gtrid;
this.bqual = bqual;
}
public int getFormatId()
{
return formatId;
}
public byte[] getBranchQualifier()
{
return bqual;
}
public byte[] getGlobalTransactionId()
{
return gtrid;
}
}
|
其次,你需要创建一个你要使用的数据库的数据源:
public DataSource getDataSource()
throws SQLException
{
SQLServerDataSource xaDS = new
com.merant.datadirect.jdbcx.sqlserver.SQLServerDataSource();
xaDS.setDataSourceName("SQLServer");
xaDS.setServerName("server");
xaDS.setPortNumber(1433);
xaDS.setSelectMethod("cursor");
return xaDS;
}
|
例1—这个例子是用“两步提交协议”来提交一个事务分支:
XADataSource xaDS;
XAConnection xaCon;
XAResource xaRes;
Xid xid;
Connection con;
Statement stmt;
int ret;
xaDS = getDataSource();
xaCon = xaDS.getXAConnection("jdbc_user", "jdbc_password");
xaRes = xaCon.getXAResource();
con = xaCon.getConnection();
stmt = con.createStatement();
xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});
try {
xaRes.start(xid, XAResource.TMNOFLAGS);
stmt.executeUpdate("insert into test_table values (100)");
xaRes.end(xid, XAResource.TMSUCCESS);
ret = xaRes.prepare(xid);
if (ret == XAResource.XA_OK) {
xaRes.commit(xid, false);
}
}
catch (XAException e) {
e.printStackTrace();
}
finally {
stmt.close();
con.close();
xaCon.close();
}
|
因为所有这些例子中的初始化代码相同或者非常相似,仅仅是一些重要的地方的代码由不同。
例2—这个例子,与例1相似,说明了一个返回过程:
xaRes.start(xid, XAResource.TMNOFLAGS);
stmt.executeUpdate("insert into test_table values (100)");
xaRes.end(xid, XAResource.TMSUCCESS);
ret = xaRes.prepare(xid);
if (ret == XAResource.XA_OK) {
xaRes.rollback(xid);
}
|
例3—这个例子说明一个分布式事务分支如何中止,让相同的连接做本地事务处理,以及它们稍后该如何继续这个分支。 分布式事务的两步提交作用不影响本地事务。
xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});
xaRes.start(xid, XAResource.TMNOFLAGS);
stmt.executeUpdate("insert into test_table values (100)");
xaRes.end(xid, XAResource.TMSUSPEND);
∥这个更新在事务范围之外完成,所以它不受XA返回影响。
stmt.executeUpdate("insert into test_table2 values (111)");
xaRes.start(xid, XAResource.TMRESUME);
stmt.executeUpdate("insert into test_table values (200)");
xaRes.end(xid, XAResource.TMSUCCESS);
ret = xaRes.prepare(xid);
if (ret == XAResource.XA_OK) {
xaRes.rollback(xid);
}
|
例4—这个例子说明一个XA资源如何分担不同的事务。 创建了两个事务分支,但是它们不属于相同的分布式事务。 JTA允许XA资源在第一个分支上做一个两步提交,虽然这个资源仍然与第二个分支相关联。
xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});
xid2 = new MyXid(100, new byte[]{0x11}, new byte[]{0x22});
xaRes.start(xid1, XAResource.TMNOFLAGS);
stmt.executeUpdate("insert into test_table1 values (100)");
xaRes.end(xid1, XAResource.TMSUCCESS);
xaRes.start(xid2, XAResource.TMNOFLAGS);
ret = xaRes.prepare(xid1);
if (ret == XAResource.XA_OK) {
xaRes.commit(xid2, false);
}
stmt.executeUpdate("insert into test_table2 values (200)");
xaRes.end(xid2, XAResource.TMSUCCESS);
ret = xaRes.prepare(xid2);
if (ret == XAResource.XA_OK) {
xaRes.rollback(xid2);
}
|
例5—这个例子说明不同的连接上的事务分支如何连接成为一个单独的分支,如果它们连接到相同的资源管理程序。这个特点改善了分布式事务的效率,因为它减
少了两步提交处理的数目。两个连接到数据库服务器上的XA将被创建。每个连接创建它自己的XA资源,正规的JDBC连接和语句。在第二个XA资源开始一个
事务分支之前,它将察看是否使用和第一个XA资源使用的是同一个资源管理程序。如果这是实例,它将加入在第一个XA连接上创建的第一个分支,而不是创建一
个新的分支。 稍后,这个事务分支使用XA资源来准备和提交。
xaDS = getDataSource();
xaCon1 = xaDS.getXAConnection("jdbc_user", "jdbc_password");
xaRes1 = xaCon1.getXAResource();
con1 = xaCon1.getConnection();
stmt1 = con1.createStatement();
xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});
xaRes1.start(xid1, XAResource.TMNOFLAGS);
stmt1.executeUpdate("insert into test_table1 values (100)");
xaRes1.end(xid, XAResource.TMSUCCESS);
xaCon2 = xaDS.getXAConnection("jdbc_user", "jdbc_password");
xaRes2 = xaCon1.getXAResource();
con2 = xaCon1.getConnection();
stmt2 = con1.createStatement();
if (xaRes2.isSameRM(xaRes1)) {
xaRes2.start(xid1, XAResource.TMJOIN);
stmt2.executeUpdate("insert into test_table2 values (100)");
xaRes2.end(xid1, XAResource.TMSUCCESS);
}
else {
xid2 = new MyXid(100, new byte[]{0x01}, new byte[]{0x03});
xaRes2.start(xid2, XAResource.TMNOFLAGS);
stmt2.executeUpdate("insert into test_table2 values (100)");
xaRes2.end(xid2, XAResource.TMSUCCESS);
ret = xaRes2.prepare(xid2);
if (ret == XAResource.XA_OK) {
xaRes2.commit(xid2, false);
}
}
ret = xaRes1.prepare(xid1);
if (ret == XAResource.XA_OK) {
xaRes1.commit(xid1, false);
}
|
例6—这个例子说明在错误恢复的阶段,如何恢复准备好的或者快要完成的事务分支。 它首先试图返回每个分支;如果它失败了,它尝试着让资源管理程序丢掉关于事务的消息。
MyXid[] xids;
xids = xaRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);
for (int i=0; xids!=null && i
try {
xaRes.rollback(xids[i]);
}
catch (XAException ex) {
try {
xaRes.forget(xids[i]);
}
catch (XAException ex1) {
System.out.println("rollback/forget failed: " + ex1.errorCode);
}
}
}
分享到:
相关推荐
Java分布式事务是大型分布式系统中不可或缺的一个重要组成部分,它确保在多个网络节点间的数据操作能够保持一致性和完整性。...通过学习和理解这些示例,开发者可以更好地掌握Java分布式事务的原理和实践。
分布式事务原理及应用 在IT领域,事务是数据库操作的核心概念,它确保了数据的一致性和完整性。事务(Transaction)是一个程序执行单元,用于访问和可能更新数据库中的数据项。在关系数据库中,事务通常由一系列SQL...
分布式事务服务 (Distributed Transaction Service, DTS) 是一个分布式事务框架,用来保障在大规模分布式环境下事务的最终一致性。DTS 从架构上分为 dts-core 、dts-schedule、 dts-server 两部分,dts-core是一个...
7. **分布式事务原理**: 分布式事务通常采用两阶段提交(2PC)、三阶段提交(3PC)或者更高级的补偿事务(Saga)等算法来协调跨多个节点的事务。Atomikos通过JTA接口实现了这些机制,使得应用程序无需关心底层实现,...
分布式事务广泛应用于微服务架构、数据库分库分表以及引入缓存等场景中。 在微服务架构中,一个操作可能会涉及多个服务模块,这些模块可能由不同的团队维护。例如,用户资产由余额、积分、优惠券等多个部分组成,每...
本篇文章将详细探讨如何使用Redisson实现Redis分布式事务锁,以及在Spring Boot环境中如何进行集成。 首先,Redis作为一个内存数据库,其高速读写性能使其成为实现分布式锁的理想选择。分布式锁的主要作用是在多...
以上是分布式事务的基本概念和相关技术点,通过分析提供的源代码,可以更深入地理解这些原理在实际开发中的应用。对于初学者,建议先理解基本理论,然后对照代码逐步学习,以提升对分布式事务的掌握。如果你在学习...
在IT行业中,分布式事务处理是复杂系统架构中的一个重要...这不仅有助于理解分布式事务的原理,也为实际开发提供了可参考的示例。如果你对这个主题感兴趣,可以通过提供的链接深入学习,了解更多的实现细节和最佳实践。
Java分布式架构设计实战课程是2022年最新的学习资源,涵盖了从基础到高级的全方位内容,旨在帮助开发者深入理解并掌握Java在分布式系统中的应用。这套课程共有13个章节,每个章节都针对特定的分布式架构技术或概念...
理解和应用CAP原理是设计分布式事务的基础。 3. **2PC(两阶段提交)协议**:这是最早的分布式事务处理协议,包括准备阶段和提交阶段。虽然简单,但存在阻塞、单点故障等问题。 4. **3PC(三阶段提交)协议**:...
LCN(Local Capable Notifier)是一个专门为Java平台设计的轻量级分布式事务解决方案,它致力于解决在微服务架构中常见的分布式事务问题。本资源包提供了关于LCN框架的全面介绍,包括PPT讲解、教学视频以及实际项目...
分布式事务是现代大规模系统中解决数据一致性问题的关键技术。在多台服务器或多个数据库之间进行操作时,确保所有节点的数据一致性是一项挑战。2PC(两阶段提交)是一种经典的分布式事务处理算法,它试图通过协调...
【标题】"开源分布式事务管理Jtom"是一个关键的话题,主要关注的是在Java环境中如何高效地管理和协调跨多个数据源的事务。Jtom,全称为Java Open Transaction Manager,是实现这一目标的重要工具。它是一个完全遵循...
在IT行业中,Java分布式应用程序设计是一项关键技能,尤其在大型企业级系统开发中不可或缺。本资源包含一本名为“JAVA分布式程序设计”的电子书以及相关的源码示例,旨在帮助开发者深入理解和实践Java在分布式环境中...
5. 分布式一致性与事务:探讨CAP理论、Paxos算法、Raft协议,以及2PC、TCC等分布式事务解决方案。 6. 分布式缓存:讲述Redis、Memcached等缓存系统在分布式系统中的应用。 7. 分布式消息队列:讲解消息中间件如...
TCC(Try-Confirm-Cancel)模式是一种实现分布式事务的策略,它允许在不同的应用程序和服务之间保持数据的一致性。本文将深入探讨TCC的基本原理、如何在Java中实现以及涉及到的关键技术。 TCC模式的核心思想是将一...
最后,压缩包中的"SpringBoot+Atomikos分布式事务及动态数据源"文件可能包含了具体的代码示例和配置文件,这些资源可以帮助开发者更好地理解和实践上述概念。通过分析和学习这些示例,可以快速上手Spring Boot与...
微服务架构的核心在于将一个大型的应用拆分成一系列小而自治的服务,这些服务可以独立部署、独立扩展,同时也带来了新的挑战——如何处理跨服务的数据一致性问题,即分布式事务。 #### 一、微服务架构与分布式事务 ...
总的来说,这个压缩包是一个宝贵的资源,对于想要深入理解和实施Java分布式事务的开发者来说,它提供了一手的实战经验和理论知识。通过学习这些材料,你可以更好地应对分布式系统中的数据一致性问题,提升你的软件...
4. **分布式服务框架**:讲解如Spring Cloud、Dubbo等Java分布式服务框架的原理和实践,包括服务发现、负载均衡、熔断机制等。 5. **微服务架构**:讨论微服务思想,如何将大型应用拆分为多个独立的服务,以及Java...