本文全面的介紹了JTA分佈式事務模型和接口規範,以及開源的分佈式事務解決方案Atomikos。筆者認同"talk is cheap,show me the code",所以在文章最後,給出一個完整的Atomikos與spring、mybatis整合的完整案例。javascript
1 JTA規範java
Java事務API(JTA:Java Transaction API)和它的同胞Java事務服務(JTS:Java Transaction Service),爲J2EE平臺提供了分佈式事務服務(distributed transaction)的能力。 某種程度上,能夠認爲JTA規範是XA規範的Java版,其把XA規範中規定的DTP模型交互接口抽象成Java接口中的方法,並規定每一個方法要實現什麼樣的功能。mysql
在DTP模型中,規定了模型的五個組成元素:應用程序(Application)、資源管理器(Resource Manager)、事務管理器(Transaction Manager)、通訊資源管理器(Communication Resource Manager)、 通訊協議(Communication Protocol)。web
而在JTA規範中,模型中又多了一個元素Application Server,以下所示:spring
各個組件的介紹以下:sql
事務管理器(transaction manager):數據庫
處於圖中最爲核心的位置,其餘的事務參與者都是與事務管理器進行交互。事務了管理器提供事務聲明,事務資源管理,同步,事務上下文傳播等功能。JTA規範定義了事務管理器與其餘事務參與者交互的接口,而JTS規範定義了事務管理器的實現要求,所以咱們看到事務管理器底層是基於JTS的。tomcat
應用服務器(application server):服務器
顧名思義,是應用程序運行的容器。JTA規範規定,事務管理器的功能應該由application server提供,如上圖中的EJB Server。一些常見的其餘web容器,如:jboss、weblogic、websphere等,均可以做爲application server,這些web容器都實現了JTA規範。特別須要注意的是,並非全部的web容器都實現了JTA規範,如tomcat並無實現JTA規範,所以並不能提供事務管理器的功能。markdown
應用程序(application):
簡單來講,就是咱們本身編寫的應用,部署到了實現了JTA規範的application server中,以後咱們就能夠咱們JTA規範中定義的UserTransaction類來聲明一個分佈式事務。一般狀況下,application server爲了簡化開發者的工做量,並不必定要求開發者使用UserTransaction來聲明一個事務,開發者能夠在須要使用分佈式事務的方法上添加一個註解,就像spring的聲明式事務同樣,來聲明一個分佈式事務。
特別須要注意的是,JTA規範規定事務管理器的功能由application server提供。可是若是咱們的應用不是一個web應用,而是一個本地應用,不須要被部署到application server中,沒法使用application server提供的事務管理器功能。又或者咱們使用的web容器並無事務管理器的功能,如tomcat。對於這些狀況,咱們能夠直接使用一些第三方的事務管理器類庫,如JOTM和Atomikos。將事務管理器直接整合進應用中,再也不依賴於application server。
資源管理器(resource manager):
理論上任何能夠存儲數據的軟件,均可以認爲是資源管理器RM。最典型的RM就是關係型數據庫了,如mysql。另一種比較常見的資源管理器是消息中間件,如ActiveMQ、RabbitMQ等, 這些都是真正的資源管理器。
正常狀況下,一個數據庫驅動供應商只須要實現JDBC規範便可,一個消息中間件供應商只須要實現JMS規範便可。 引入了分佈式事務的概念後,DB、MQ等在DTP模型中的做用都是RM,兩者是等價的,須要由TM統一進行協調。
爲此,JTA規範定義了一個XAResource接口,其定義RM必需要提供給TM調用的一些方法。以後,無論這個RM是DB,仍是MQ,TM並不關心,由於其操做的是XAResource接口。而其餘規範(如JDBC、JMS)的實現者,同時也對此接口進行實現。如MysqlXAConnection,就實現了XAResource接口。
通訊資源管理器(Communication Resource Manager):
這個是DTP模型中就已經存在的概念,對於須要跨應用的分佈式事務,事務管理器彼此之間須要通訊,這是就是經過CRM這個組件來完成的。JTA規範中,規定CRM須要實現JTS規範定義的接口。
下圖更加直觀的演示了JTA規範中各個模型組件之間是如何交互的:
說明以下:
JTA事務模型規定了一個分佈式事務中有哪些組件,而JTA接口規範則規定這些組件之間如何交互。須要注意的是JTA規範定義的這些接口,並不須要應用程序的開發人員去實現,而是由各個廠商去實現,根據在DTP模型中扮演的不一樣角色,須要實現不一樣的接口。做爲開發人員的咱們只須要學會如何使用便可。
JTA規範是Java擴展包,在應用中須要額外引入相應的jar包依賴
<dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version></dependency>
JTA規範1.1中的源碼很是少,以下所示:
能夠看到,這裏除了紅色框中包含的接口定義以外,其餘所有是異常(XxxException),這裏咱們僅討論JTA規範中定義的接口做用:
TM供應商:
實現UserTransaction、TransactionManager、Transaction、TransactionSynchronizationRegistry、Synchronization、Xid接口,經過與XAResource接口交互來實現分佈式事務。此外,TM廠商若是要支持跨應用的分佈式事務,那麼還要實現JTS規範定義的接口。
常見的TM提供者包括咱們前面提到的application server,包括:jboss、ejb server、weblogic等,以及一些以第三方類庫形式提供事務管理器功能的jotm、Atomikos。
RM供應商:
XAResource接口須要由資源管理器者來實現,XAResource接口中定義了一些方法,這些方法將會被TM進行調用,如:
一些RM提供者,可能也會提供本身的Xid接口的實現。
此外,不一樣的資源管理器有一些各自的特定接口要實現:
做爲DTP模型中Application開發者的咱們,並不須要去實現任何JTA規範中定義的接口,只須要使用TM提供的UserTransaction實現,來聲明、提交、回滾一個分佈式事務便可。
如下案例演示了UserTransaction接口的基本使用:構建一個分佈式事務,來操做位於2個不一樣的數據庫的數據,假設這兩個庫中都有一個user表。
UserTransaction userTransaction=... try{ //開啓分佈式事務 userTransaction.begin(); //執行事務分支1 conn1 = db1.getConnection(); ps1= conn1.prepareStatement("INSERT into user(name,age) VALUES ('tianshouzhi',23)"); ps1.executeUpdate(); //執行事務分支2 conn2 = db2.getConnection(); ps2 = conn2.prepareStatement("INSERT into user(name,age) VALUES ('tianshouzhi',23)"); ps2.executeUpdate(); //提交,兩階段提交發生在這個方法內部 userTransaction.commit(); }catch (Exception e){ try { userTransaction.rollback();//回滾 } catch (SystemException ignore) { } }
須要注意的是,在分佈式事務中,當咱們須要提交或者回滾一個事務時,不該該再使用Connection接口提供的commit和rollback方法。而是應該使用UserTransaction接口的commit接口和rollback接口替代。
另外,這個案例只是用於說明如何使用UserTransaction類,事實上,在實際開發中,並無這麼複雜。一些開源的分佈式事務解決方案,能夠與spring聲明式事務管理功能,所以咱們能夠經過一個簡單@Transactional註解,便可實現分佈式事務的功能。
例如,下面咱們將要提到Atomikos,就支持與spring事務整合。
Atomikos公司旗下有兩款著名的分佈事務產品:
這兩個產品的關係以下圖所示:
能夠看到,在開源版本中支持JTA/XA、JDBC、JMS的分佈式事務。
最簡單的狀況下,你只須要引入以下依賴:
<dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>4.0.6</version></dependency>
atomikos也支持與spring事務整合。spring事務管理器的頂級抽象是PlatformTransactionManager接口,其提供了個重要的實現類:
顯然,在這裏,咱們須要配置的是JTATransactionManager。
下面的代碼片斷了,演示了與spring事務整合後的分佈式事務的案例代碼。假設有兩個mybatis映射器接口UserMapper和AccountMapper,操做不一樣的庫。
public class JTAService { @Autowired private UserMapper userMapper;//操做db_user庫 @Autowired private AccountMapper accountMapper;//操做db_account庫 @Transactional public void insert() { User user = new User(); user.setName("wangxiaoxiao"); userMapper.insert(user); //模擬異常,spring回滾後,db_user庫中user表中也不會插入記錄 Account account = new Account(); account.setUserId(user.getId()); account.setMoney(123456789); accountMapper.insert(account); }}
能夠發現分佈式事務的邏輯,與操做單庫事務基本上是徹底相同的,底層的複雜邏輯對應用程序開發者來講徹底屏蔽。