#0 系列目录#java
#1 X/Open DTP# DTP全称是Distributed Transaction Process,即分布式事务模型。以前咱们接触的事务都是针对单个数据库的操做,若是涉及多个数据库的操做,还想保证原子性,这就须要使用分布式事务了。而X/Open DTP就是一种分布式事务处理模型。mysql
##1.1 X/Open DTP模型## web
上面主要涉及了三个对象:sql
他们三者的关系:数据库
##1.2 两阶段提交## 全称是The two-phase commit protocol,简称2PC。tomcat
当须要协调多个资源管理器进行分布式事务的时候,就须要使用到两阶段提交协议,以下图所示:服务器
第一个阶段:事务管理器经过XA协议,向资源管理器发送prepare命令,询问他们预提交是否成功,每一个资源管理器给出本身的响应,预提交成功或者失败。框架
第二个阶段:根据第一个阶段的资源管理器的回复状况,若是所有表示预提交成功,则事务管理器向全部的资源管理器发送最终的提交命令。若是有一个资源管理器回复预提交失败,则事务管理器向全部的资源管理器发送回滚命令,所有进行回滚。分布式
经过这样的两阶段提交协议,便可完成了分布式事务的功能。这样的协议的正是基于了事务管理器和资源管理器的双向通讯能力,而这在以前的本地事务中,如jdbc事务中,咱们只能经过Connection向数据库传递命令,不能从数据库获取事务的一些执行信息,这种状况是单向的。性能
然而仍是存在一些问题的:
两阶段提交协议是阻塞式协议,因此事务管理器必须等待每个资源管理器发送回复以后,才能进行下一步操做。一旦资源管理器挂掉,事务管理器则收不到响应,就会形成事务管理器一直等待。这时候就必须引入超时机制,一旦超过某个时间尚未回复,则认为失败,回滚全部操做,这些都是很是耗性能的。
一旦事务管理器挂掉,则资源管理器则一直等待事务管理器的命令。这时候可能就须要使用备份的事务管理器来接替原来的事务管理器的工做。
#2 JTA接口定义# 上述接口规范不是针对某种语言的,java是如何来落实上述规范的呢?这就是JTA的内容了。先来预览下JTA的包结构:
主要2个大包的内容:(1)javax.transaction;(2)javax.transaction.xa;
##2.1 AP、TM、RM三大对象##
AP:咱们的应用程序。
TM:即javax.transaction.TransactionManager 事务管理器。
RM:即javax.transaction.xa.XAResource 我称之为与资源管理器的一个通讯表明,咱们经过XAResource接口方法和资源管理器进行通讯。
begin():建立一个新的事务并关联到当前线程
Transaction getTransaction():获取与当前线程关联的事务
commit():提交与当前线程关联的事务
rollback():回滚与当前线程关联的事务
等等
定义了一些经常使用的事务操做,这里操做的事务的定义为javax.transaction.Transaction,接口以下所示:
commit():提交事务
rollback():回滚事务
enlistResource(XAResource xaRes):把给定的XAResource(资源管理器的一个通讯表明)加入当前事务中来,一个分布式事务会涉及与多个资源管理器交互,该操做就是把某个资源管理器的通讯表明归入当前事务中来
delistResource(XAResource xaRes, int flag):把给定的XAResource(资源管理器的一个通讯表明)从当前事务中去除
咱们看到有一个Xid接口:X/Open组织规定了,对于每一个分布式事务,都有一个对应的惟一标示。这个惟一标示在java中就是Xid接口。
接下来看下XAResource的其余方法:
start(Xid xid, int flags)和end(Xid xid, int flags)用于告知资源管理器的事务的边界,即在这两个方法之间的sql等操做才会归入xid对应的分布式事务中
prepare(Xid xid):则就是上述图片中的第一个阶段,针对xid对应的分布式事务,向资源管理器发送预提交命令
rollback(Xid xid)、commit(Xid xid, boolean onePhase) 若是全部资源管理器都回复OK,则向全部资源管理器发送commit提交命令,不然发送rollback回滚命令
下面看一个简单模拟两阶段提交的例子,加深对上面的方法的理解:
xaRes1.start(xid, XAResource.TMNOFLAGS); stmt1.executeUpdate("insert into test_table1 values (100)"); xaRes1.end(xid, XAResource.TMSUCCESS); stmt1.executeUpdate("insert into test_table1 values (99)"); xaRes2.start(xid, XAResource.TMNOFLAGS); stmt2.executeUpdate("insert into test_table2 values (100)"); xaRes2.end(xid, XAResource.TMSUCCESS); ret1 = xaRes1.prepare(xid); ret2 = xaRes2.prepare(xid); if (ret1 == XAResource.XA_OK && ret2 == XAResource.XA_OK) { xaRes1.commit(xid, false); xaRes2.commit(xid, false); }else{ xaRes1.rollback(xid); xaRes2.rollback(xid); }
过程说明:
首先使用start、end来告知资源管理器分布式事务的边界,上述stmt1.executeUpdate("insert into test_table1 values (99)");没有在start、end范围以内,则不会划入分布式事务的管辖中,而是做为了通常的本地事务。
而后遍历每一个资源管理器的通讯表明,使他们询问资源管理器预提交的状况
若是所有OK则所有提交,不然所有回滚
更多的例子,能够参考这里JAVA分布式事务原理及应用。
##2.2 UserTransaction接口## UserTransaction接口是给开发人员使用的事务接口,屏蔽了底层的实现细节,经过该接口就能够操做一个分布式事务。该接口的实现一般是委托给TM即事务管理器来完成。接口内容以下:
能够看到,UserTransaction的这些方法在TM即事务管理器中都有对应的方法。
##2.3 JTA中涉及的角色##
更多的支持分布式的数据库驱动能够看下这里XA Drivers。
##2.4 JTS## JTS是什么呢?它和JTA是什么关系呢?
这个我也是没弄清楚,能够参考这里的说法,若是以为不对,自行去深刻研究下,原文在这里JTA、JTS各司其职,共职分布式事务。
JTS也定义了一套规范,它约定了各个程序角色之间如何传递事务上下文,它源自CORBA 的OTS规范,基于IIOP(一种软件交互协议)。不要认为JTS是JTA的实现,JTA其实就定义了一个空架子,告诉JTA的实现者应该怎样作怎样作,可是具体到作的时候JTS就来插一手了。由于JTA约定的这些角色要进行事务上下文的交互啊,JTS约定了应该怎样去进行交互。
##2.5 疑问解答##
不是,可使用第三方框架如jotm、Atomikos来模拟XA协议。
不是。
jboss是支持的,因此咱们能够直接使用jboss本身实现的UserTransaction来实现分布式事务(使用JNDI方式,这时候要求数据库驱动支持XA协议),能够不须要像jotm、Atomikos等第三方框架。
tomcat就是不支持的,可是咱们可使用第三方框架来实现,如jotm、Atomikos。