Spring + Atomikos 分散式事務實現方式
前段時間發現對分散式事務瞭解的不夠清晰,最近又重新看了一下分散式事務,簡單做個記錄,以後方便檢視
Java規範對分散式事務定義了標準的規範Java事務API和Java事務服務,分別是JTA和JTS
一個分散式事務必須包括一個事務管理器和多個資源管理器,
資源管理器是任意型別的持久化資料儲存,而事務管理器則是承擔著所有事務參與單元者的相互通訊的責任
JTA的規範制定了分散式事務的實現的整套流程框架,定義了各個介面且只有介面,而實現分別交給事務管理器的實現方和資源管理器的實現方
對於資源管理器而言,主要包括資料庫連線,JMS等,還有很多瞭解的不清楚
對於事務管理器而言,從網上了解主要是應用伺服器,包括JBOSS,WEBLOGIC等應用伺服器,也就是說事務管理器的實現方是應用伺服器,用來管理事務的通訊和協調
對於大多數談的資料庫瞭解,事務管理器需要從資料庫獲得XAConnection , XAResource等物件,而這些物件是資料庫驅動程式需要提供的
所以如果要實現分散式事務還必須有支援分散式事務的資料庫伺服器以及資料庫驅動程式
對Mysql而言,在mysql5.0以上的版本已經支援了分散式事務,另外常用的mysql-connector-java-5.1.25-bin.jar也是支援分散式事務的
可以在jar包的com.mysql.jdbc.jdbc2.optional中找到XA物件的實現
上面介紹了事務管理器和資源管理器的實現方式,在學習研究過程中發現對於事務管理器,特別強調了tomcat等伺服器是不支援的,這句話的意思應該是在tomcat容器內
並沒有分散式事務管理器的實現物件。而在JBOSS或者WEBLOGIC等商業伺服器應該內建了分散式事務管理器的實現物件,應用程式可以通過JNDI方式獲取UserTransaction
和TransactionManager等分散式事務環境中所需要用到的物件
事務管理器作為管理和協調分散式事務的關鍵處理中心非常重要,所以應用伺服器可以單獨只用過事務管理器。
上圖具體文章連結為http://blog.csdn.net/xiaol_zhong/article/details/7983863
上面主要是一些基本的概念,在學習研究中總結出來的,可能不太全面,下面主要介紹一下在使用Spring使用分散式事務中的心得,這種做法也是將事務管理器嵌入應用中。
開始準備Spring的時候,網上介紹了Jotm以及Atomikos等工具,實際上這些工具都是取代應用伺服器對事務管理器的支援,負責實現事務管理器物件
Jotm需要使用特定資料庫連線池enhydra,而且網上說因為維護時間久遠,問題不少,所以直接使用Atomikos進行測試
在Maven上下載了Atomikos的3.9.0版本的相關需要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
下面看一下主要的配置檔案的配置方式:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="dataSource0" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://172.17.2.5:3003/jta" /> <property name="user" value="root" /> <property name="password" value="ems" /> <property name="autoCommitOnClose" value="true" /> </bean> <bean id="dataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="ds1" /> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name="xaProperties"> <props> <prop key="url">jdbc:mysql://172.17.2.5:3003/jta</prop> <prop key="user">root</prop> <prop key="password">ems</prop> </props> </property> <property name="minPoolSize" value="10" /> <property name="maxPoolSize" value="100" /> <property name="borrowConnectionTimeout" value="30" /> <property name="testQuery" value="select 1" /> <property name="maintenanceInterval" value="60" /> </bean> <bean id="dataSource2" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="ds2" /> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name="xaProperties"> <props> <prop key="url">jdbc:mysql://172.17.2.5:3306/jta</prop> <prop key="user">root</prop> <prop key="password">ems</prop> </props> </property> <property name="minPoolSize" value="10" /> <property name="maxPoolSize" value="100" /> <property name="borrowConnectionTimeout" value="30" /> <property name="testQuery" value="select 1" /> <property name="maintenanceInterval" value="60" /> </bean> <bean id="dataSource3" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://172.17.2.5:3306/jta" /> <property name="user" value="root" /> <property name="password" value="ems" /> <property name="autoCommitOnClose" value="true" /> </bean> <!--SqlMap setup for MyBatis Database Layer --> <bean id="sqlSessionFactoryForD1" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource1" /> <property name="mapperLocations" value="classpath:jtaatomikos/jta.xml" /> </bean> <bean id="sqlSessionTemplateForD1" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactoryForD1" /> </bean> <bean id="sqlSessionFactoryForD2" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource2" /> <property name="mapperLocations" value="classpath:jtaatomikos/jta.xml" /> </bean> <bean id="sqlSessionTemplateForD2" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactoryForD2" /> </bean> <!-- Config JTA UserTransactionManager Impl --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <property name="forceShutdown"> <value>true</value> </property> </bean> <!-- Config JTA UserTransaction Impl --> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout"> <value>300</value> </property> </bean> <!-- Spring JtaTransactionManager Config --> <bean id="springJTATransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager"> <ref bean="atomikosTransactionManager" /> </property> <property name="userTransaction"> <ref bean="atomikosUserTransaction" /> </property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource1" /> </bean> <!-- Aop Config --> <aop:config> <aop:pointcut id="jtaServiceOperation" expression="execution(* jtaatomikos.*Service.*(..))"></aop:pointcut> <aop:advisor pointcut-ref="jtaServiceOperation" advice-ref="txAdvice"></aop:advisor> </aop:config> <!-- Transacation Advice Handle --> <tx:advice id="txAdvice" transaction-manager="springJTATransactionManager"> <tx:attributes> <tx:method name="update*" rollback-for="Exception" /> </tx:attributes> </tx:advice> <context:component-scan base-package="jtaatomikos"></context:component-scan> </beans>
下面分別對每段配置進行部分記錄
分別定義了dataSource0 dataSource1 dataSource2 dataSource3
其中0和3都是普通的C3P0資料庫連線池,而1和2是AtomikosDataSourceBean
定義0和3的目的是在後面的測試中測試如果不是AtomikosDataSourceBean的連線是不能加入到分散式事務中的
接著定義了兩個sqlSessionTemplate,分別對應3003和3306兩個資料庫。對應程式中的兩個Dao
下面是定義分散式事務最重要的兩個實現UserTransactionManager和UserTransactionImpl,均使用Atomikos中的實現
接著是Spring的AOP代理所使用的事務處理器springJTATransactionManager,這是Spring自帶的JTA實現類,但是Spring只負責提供介面,真正內部實現分散式事務的上面定義
的兩個物件,所以需要將上面定義的兩個物件進行注入,所以Spring框架負責提供介面,Atomikos負責實現
另外再定義一個transactionManager是為了測試在傳統的Spring事務方式下,為什麼不能支援分散式事務
後面就是為了測試定義的AOP配置,不再多說
再來看看具體測試實現,只貼出核心測試方法
存在Service如下
public interface D1Service { void updateAccount(Integer account); }
實現如下:
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資料庫上新建相同的資料庫表,簡單測試需要欄位userId和account,初始化定義資料為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);
}
很明顯在執行轉賬1000的時候,是沒有問題的,在第二部執行轉賬9100的時候,由於userB_Account-account< 0 成立所以會有異常丟擲
此時就是測試分散式事務的關鍵
假設以下幾種情況
在使用springJTATransactionManager的情況下
1 均使用正確的AtomikosDataSourceBean,此時兩個事務均能正確回滾
2 如果分別使用AtomikosDataSourceBean和C3P0,則只有前者對應資料庫會回滾,而後者則不會回滾,猜想這是因為 springJTATransactionManager在處理事務的時候, 內部的atomikosTransactionManager只會將AtomokosDataSourceBean加入到分散式事務中,而不考慮其他連線方式
3 如果均使用C3P0,根據上面的解釋,很清楚的可以猜到兩個資料庫的資料均不會回滾,測試結果也符合該預期
再來談談分散式事務為什麼需要使用springJTATransactionManager
Spring傳統的事務管理均使用
org.springframework.jdbc.datasource.DataSourceTransactionManager,那這種事務管理器為什麼不能支援分散式事務呢?
從配置中可以看出該物件需要注入dataSource屬性,注意只能注入單一的dataSource,顯然這是不符合分散式事務的必須使用多個數據庫這一基礎的,所以在使用傳統的該事務管理器,只能選擇一個數據連線進行事務管理,本身來說Spring的事務管理也是基於這點實現的,保證事務管理內的所有資料庫操作均使用同一個Connection,例如Connection的begin和commit以及rollback控制事務。
當使用org.springframework.jdbc.datasource.DataSourceTransactionManager
測試結果如下:
第一個結論:無論使用DataSource0 - DataSource3 中的任何一個,均能正常回滾,也就是說該事務管理器不依賴DataSource的具體實現,不論是Atomikos的實現或者是其他的資料庫連線實現,均能夠被傳統的事務管理器管理
第二個結論:因為該事務管理器只能配置單一的DataSource,所以只能保證配置的DataSource能被事務管理,其它的DataSource都不受事務控制,其原理也很顯而易見,因為傳統的事務管理器使用單一Connection進行事務管理,在分散式事務多個不同資料庫的Connection條件下,顯然這種實現方式不能成立。所以需要Atimikos提供實現了JTA規範標準的事務管理器
關於JTA的兩段提交方案,網上也很多教程,後面自己也會進行一步步實踐,後面會跟進進行記錄
相關推薦
Spring + Atomikos 分散式事務實現方式
前段時間發現對分散式事務瞭解的不夠清晰,最近又重新看了一下分散式事務,簡單做個記錄,以後方便檢視 Java規範對分散式事務定義了標準的規範Java事務API和Java事務服務,分別是JTA和JTS 一個分散式事務必須包括一個事務管理器和多個資源管
分散式事務實現方式
一、TCC(try-confirm/cancel) try過程是資源預留的過程,只有當兩次資源預留都成功了之後,再執行confrim,如果其中一個失敗了,就執行cancel,回滾 二、訊息事務(最終一致) 通過訊息中介軟體來實現分散式事務,如上圖所示: 1⃣️
Spring 使用註解方式進行事務管理 /==/ Spring分散式事務實現
使用步驟: 步驟一、在spring配置檔案中引入<tx:>名稱空間 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-ins
spring 事物控制---多資料來源與 atomikos 分散式事務配置(接之前未完成的部分)
接上一篇 說明:解決了多個數據源的資料同步問題(例如:在一個事物裡add操作A庫的a表,同時add操作B庫b表,如果B庫連線中斷或者因為別的異常造成b表寫入失敗,希望a表回滾)需要引入相關jar包。 1.pom.xml(需要新的jar包,就不一一列舉了)
8. Spring:AOP的實現方式
8. Spring:AOP的實現方式 利用Proxy實現AOP功能 採用Proxy類方法,基本流程為:主函式–>代理–>目標物件的方法。對於Proxy類有一個使用前提,就是目標物件必須要實現介面,否則不能使用這個方法。、 實現AOP功能步驟如下
Spring裡的aop實現方式和原始碼分析 java中代理,靜態代理,動態代理以及spring aop代理方式,實現原理統一彙總
使用"橫切"技術,AOP把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,比如許可權認證、日誌、事務。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。
spring宣告式事務管理方式( 基於tx和aop名字空間的xml配置[email
轉自:https://www.cnblogs.com/niceyoo/p/8732891.html 1. 宣告式事務管理分類 宣告式事務管理也有兩種常用的方式, 一種是基於tx和aop名字空間的xml配置檔案,另一種就是基於@Transactional註解。 顯然基於註解的方式更簡單
Spring Cloud分散式事務解決方案
開源專案 我們利用訊息佇列實現了分散式事務的最終一致性解決方案,請大家圍觀。可以參考Github CoolMQ原始碼,專案支援網站: http://rabbitmq.org.cn,最新文章或實現會更新在上面 二 前言 阿里2017雲棲大會《破解世界性技術難題!GTS
【spring系列】之14:spring宣告式事務實現原理剖析
通過上一節事務環境搭建,我們知道,在搭建的5個步驟中,有兩個是spring為我們提供底層去稍作配置,然後使用的, 這兩個操作涉及的便是: @EnableTransactionManagement PlatformTransactionManager 其中,Platfor
Spring Cloud分散式事務終極解決方案探討
一 前言 本話題已收入視訊講座《Spring Cloud分散式事務解決方案》大家不妨圍觀下 阿里2017雲棲大會《破解世界性技術難題!GTS讓分散式事務簡單高效》中,阿里聲稱提出了一種破解世界性難題之分散式事務的終極解決方案,無論是可靠性、還是處理速率都領先於市面上所有的技
spring中管理事務的方式
這裡寫了三種方式 1、 編碼式 &nb
Spring定時排程任務實現方式
Spring定時任務的幾種實現 近日專案開發中需要執行一些定時任務,比如需要在每天凌晨時候,分析一次前一天的日誌資訊,藉此機會整理了一下定時任務的幾種實現方式,由於專案採用spring框架,所以我都將結合 spring框架來介紹。 一.分類 從實現的技術上來分類,目
spring security四種實現方式
spring security實現方式大致可以分為這幾種: 1.配置檔案實現,只需要在配置檔案中指定攔截的url所需要許可權、配置userDetailsService指定使用者名稱、密碼、對應許可權,就可以實現。 2.實現UserDetailsService
Spring-Hibernate分庫事務實現
忙活了一個星期,看了N多Spring、Hibernate原始碼,查了N多資料,走了N多彎路,總算實現了分散式事務。回頭看看,實現這個功能對自己的提升確實是巨大的。 進入正題,官網之前的選庫方案是在DataSource層面進行選庫的,繼承org.sprin
Atomikos分散式事務中切換資料來源
昨天的分析痛快淋漓,環環相扣,分享後好評如潮。然而內心卻有隱隱疑慮,不知從何而來。本沒有計劃再寫續集,然而一覺醒來,已然恍然大悟。 我們邊看RIO開幕式,邊細細道來。 1. 問題由來 昨日我們庖丁
Spring的JDBC事務實現
之前專案中有大量資料提交的需求,考慮了幾個解決方案後還是覺得使用事務提交效率更高、資料插入也更方便。 一、首先,讓我們來看看什麼是事務 事務(Transaction)是併發控制的單元,是使用者定義的一個操作序列。這些操作要麼都做,要麼都不做,是一個不可分割的工作單位。通過事
分散式鎖實現方式
三種實現方式:1、資料庫實現方式 比較版本號 ;插入一條一樣的資料,後刪;2、redis 先判斷有沒有這個key,設定然後再刪,沒有就設,有就等 3、zookpeer 有序節點,比較自己是不是最小的 等待 我感覺上面三個方式其實是同一個意思,zookpeer 要建立節點,還
Spring如何設定讓事務自動提交和回滾?Spring兩種事務管理方式的配置及使用
1,我們要明確,Spring如何設定讓事務自動提交和回滾? ①如何自動提交? 理論上,可以通過對DataSource如下設定,讓事務自動提交 <!-- 配置資料來源 --> <beanid="dataSour
Spring程式設計式事務實現
程式設計式事務概述 所謂程式設計式事務指的是通過編碼方式實現事務,即類似於JDBC程式設計實現事務管理。 Spring框架提供一致的事務抽象,因此對於JDBC還是JTA事務都是採用相同的API進行程式設計。 java程式碼
Spring定時任務的實現方式--ScheduledExecutorService
方式一:使用Spring的任務排程類ScheduledExecutorTask 1.建立一個Java類,並實現Runnable介面 package com.Solin.Timer; impo