前段時間發(fā)現(xiàn)對分布式事務(wù)了解的不夠清晰,最近又重新看了一下分布式事務(wù),簡單做個記錄,以后方便查看
Java規(guī)范對分布式事務(wù)定義了標(biāo)準(zhǔn)的規(guī)范Java事務(wù)API和Java事務(wù)服務(wù),分別是JTA和JTS
一個分布式事務(wù)必須包括一個事務(wù)管理器和多個資源管理器,
資源管理器是任意類型的持久化數(shù)據(jù)存儲,而事務(wù)管理器則是承擔(dān)著所有事務(wù)參與單元者的相互通訊的責(zé)任
JTA的規(guī)范制定了分布式事務(wù)的實現(xiàn)的整套流程框架,定義了各個接口且只有接口,而實現(xiàn)分別交給事務(wù)管理器的實現(xiàn)方和資源管理器的實現(xiàn)方
對于資源管理器而言,主要包括數(shù)據(jù)庫連接,JMS等,還有很多了解的不清楚
對于事務(wù)管理器而言,從網(wǎng)上了解主要是應(yīng)用服務(wù)器,包括JBOSS,WEBLOGIC等應(yīng)用服務(wù)器,也就是說事務(wù)管理器的實現(xiàn)方是應(yīng)用服務(wù)器,用來管理事務(wù)的通訊和協(xié)調(diào)
對于大多數(shù)談的數(shù)據(jù)庫了解,事務(wù)管理器需要從數(shù)據(jù)庫獲得XAConnection , XAResource等對象,而這些對象是數(shù)據(jù)庫驅(qū)動程序需要提供的
所以如果要實現(xiàn)分布式事務(wù)還必須有支持分布式事務(wù)的數(shù)據(jù)庫服務(wù)器以及數(shù)據(jù)庫驅(qū)動程序
對Mysql而言,在mysql5.0以上的版本已經(jīng)支持了分布式事務(wù),另外常用的mysql-connector-java-5.1.25-bin.jar也是支持分布式事務(wù)的
可以在jar包的com.mysql.jdbc.jdbc2.optional中找到XA對象的實現(xiàn)
上面介紹了事務(wù)管理器和資源管理器的實現(xiàn)方式,在學(xué)習(xí)研究過程中發(fā)現(xiàn)對于事務(wù)管理器,特別強調(diào)了tomcat等服務(wù)器是不支持的,這句話的意思應(yīng)該是在tomcat容器內(nèi)
并沒有分布式事務(wù)管理器的實現(xiàn)對象。而在JBOSS或者WEBLOGIC等商業(yè)服務(wù)器應(yīng)該內(nèi)置了分布式事務(wù)管理器的實現(xiàn)對象,應(yīng)用程序可以通過JNDI方式獲取UserTransaction
和TransactionManager等分布式事務(wù)環(huán)境中所需要用到的對象
事務(wù)管理器作為管理和協(xié)調(diào)分布式事務(wù)的關(guān)鍵處理中心非常重要,所以應(yīng)用服務(wù)器可以單獨只用過事務(wù)管理器。
上圖具體文章鏈接為http://blog.csdn.net/xiaol_zhong/article/details/7983863
上面主要是一些基本的概念,在學(xué)習(xí)研究中總結(jié)出來的,可能不太全面,下面主要介紹一下在使用Spring使用分布式事務(wù)中的心得,這種做法也是將事務(wù)管理器嵌入應(yīng)用中。
開始準(zhǔn)備Spring的時候,網(wǎng)上介紹了Jotm以及Atomikos等工具,實際上這些工具都是取代應(yīng)用服務(wù)器對事務(wù)管理器的支持,負責(zé)實現(xiàn)事務(wù)管理器對象
Jotm需要使用特定數(shù)據(jù)庫連接池enhydra,而且網(wǎng)上說因為維護時間久遠,問題不少,所以直接使用Atomikos進行測試
在Maven上下載了Atomikos的3.9.0版本的相關(guān)需要jar包。主要包括
atomikos-util-3.9.0.jar transactions-3.9.0.jar transactions-api-3.9.0.jar transactions-jdbc-3.9.0.jar transactions-jta-3.9.0.jar jta-1.1.jar
下面看一下主要的配置文件的配置方式:
下面分別對每段配置進行部分記錄
分別定義了dataSource0 dataSource1 dataSource2 dataSource3
其中0和3都是普通的C3P0數(shù)據(jù)庫連接池,而1和2是AtomikosDataSourceBean
定義0和3的目的是在后面的測試中測試如果不是AtomikosDataSourceBean的連接是不能加入到分布式事務(wù)中的
接著定義了兩個sqlSessionTemplate,分別對應(yīng)3003和3306兩個數(shù)據(jù)庫。對應(yīng)程序中的兩個Dao
下面是定義分布式事務(wù)最重要的兩個實現(xiàn)UserTransactionManager和UserTransactionImpl,均使用Atomikos中的實現(xiàn)
接著是Spring的AOP代理所使用的事務(wù)處理器springJTATransactionManager,這是Spring自帶的JTA實現(xiàn)類,但是Spring只負責(zé)提供接口,真正內(nèi)部實現(xiàn)分布式事務(wù)的上面定義
的兩個對象,所以需要將上面定義的兩個對象進行注入,所以Spring框架負責(zé)提供接口,Atomikos負責(zé)實現(xiàn)
另外再定義一個transactionManager是為了測試在傳統(tǒng)的Spring事務(wù)方式下,為什么不能支持分布式事務(wù)
后面就是為了測試定義的AOP配置,不再多說
再來看看具體測試實現(xiàn),只貼出核心測試方法
存在Service如下
- public interface D1Service {
-
- void updateAccount(Integer account);
-
-
- }
實現(xiàn)如下:
- public void updateAccount(Integer account) {
- int userAId = 1;
- int userBId = 2;
- int userA_Account = d1Dao.getAccount(userAId);
- int userB_Account = d2Dao.getAccount(userBId);
- d1Dao.saveAccount(userAId, userA_Account + account);
- d2Dao.saveAccount(userBId, userB_Account - account);
- if(userB_Account - account < 0){
- throw new AccountNotEnoughException();
- }
- }
分別在3003和3306數(shù)據(jù)庫上新建相同的數(shù)據(jù)庫表,簡單測試需要字段userId和account,初始化定義數(shù)據(jù)為1 10000 ; 2 10000;
代表用戶1和2分別賬戶有10000。
測試程序如下:
- <pre name="code" class="java">public static void main(String[] args) {
- ApplicationContext appContext = new ClassPathXmlApplicationContext("jtaatomikos/application-jta-atomikos.xml");
- D1Service service = (D1Service) appContext.getBean("d1Service");
- service.updateAccount(1000);
- service.updateAccount(9100);
- }
很明顯在執(zhí)行轉(zhuǎn)賬1000的時候,是沒有問題的,在第二部執(zhí)行轉(zhuǎn)賬9100的時候,由于userB_Account-account< 0 成立所以會有異常拋出
此時就是測試分布式事務(wù)的關(guān)鍵
假設(shè)以下幾種情況
在使用springJTATransactionManager的情況下
1 均使用正確的AtomikosDataSourceBean,此時兩個事務(wù)均能正確回滾
2 如果分別使用AtomikosDataSourceBean和C3P0,則只有前者對應(yīng)數(shù)據(jù)庫會回滾,而后者則不會回滾,猜想這是因為 springJTATransactionManager在處理事務(wù)的時候, 內(nèi)部的atomikosTransactionManager只會將AtomokosDataSourceBean加入到分布式事務(wù)中,而不考慮其他連接方式
3 如果均使用C3P0,根據(jù)上面的解釋,很清楚的可以猜到兩個數(shù)據(jù)庫的數(shù)據(jù)均不會回滾,測試結(jié)果也符合該預(yù)期
再來談?wù)劮植际绞聞?wù)為什么需要使用springJTATransactionManager
Spring傳統(tǒng)的事務(wù)管理均使用
org.springframework.jdbc.datasource.DataSourceTransactionManager,那這種事務(wù)管理器為什么不能支持分布式事務(wù)呢?
從配置中可以看出該對象需要注入dataSource屬性,注意只能注入單一的dataSource,顯然這是不符合分布式事務(wù)的必須使用多個數(shù)據(jù)庫這一基礎(chǔ)的,所以在使用傳統(tǒng)的該事務(wù)管理器,只能選擇一個數(shù)據(jù)連接進行事務(wù)管理,本身來說Spring的事務(wù)管理也是基于這點實現(xiàn)的,保證事務(wù)管理內(nèi)的所有數(shù)據(jù)庫操作均使用同一個Connection,例如Connection的begin和commit以及rollback控制事務(wù)。
當(dāng)使用org.springframework.jdbc.datasource.DataSourceTransactionManager
測試結(jié)果如下:
第一個結(jié)論:無論使用DataSource0 - DataSource3 中的任何一個,均能正?;貪L,也就是說該事務(wù)管理器不依賴DataSource的具體實現(xiàn),不論是Atomikos的實現(xiàn)或者是其他的數(shù)據(jù)庫連接實現(xiàn),均能夠被傳統(tǒng)的事務(wù)管理器管理
第二個結(jié)論:因為該事務(wù)管理器只能配置單一的DataSource,所以只能保證配置的DataSource能被事務(wù)管理,其它的DataSource都不受事務(wù)控制,其原理也很顯而易見,因為傳統(tǒng)的事務(wù)管理器使用單一Connection進行事務(wù)管理,在分布式事務(wù)多個不同數(shù)據(jù)庫的Connection條件下,顯然這種實現(xiàn)方式不能成立。所以需要Atimikos提供實現(xiàn)了JTA規(guī)范標(biāo)準(zhǔn)的事務(wù)管理器
關(guān)于JTA的兩段提交方案,網(wǎng)上也很多教程,后面自己也會進行一步步實踐,后面會跟進進行記錄