【分布式事務】使用atomikos+jta解決分布式事務問題
一、前言
分布式事務,這個問題困惑了小編很久,在3個月之前,就間斷性的研究分布式事務。從MQ方面,數據庫事務方面,jta方面。近期終於成功了,使用JTA解決了分布式事務問題。先寫一下心得,後面的二級提交也會在研究。
二、介紹
分布式事務
說到分布式事務,可以理解為,由於分布式而引起的事務不一致的問題。隨著項目做大,模塊拆分,數據庫拆分。一次包含增刪改操作數據庫涉及到了更新兩個不同物理節點的數據庫,這樣的數據庫事務只能保證自己處理的部分的事務,但是整個的事務就不能保證一致性。
網上針對分布式事務常見的例子有:轉賬
我從農行轉賬100元到建設銀行。首先,農行和建行的數據庫是分開的,其次要在農行數據庫中-100,在建行數據庫+100。分布式事務就是要保障建行+100出錯了,使得農行回滾為原來的數目。
JTA
JTA(java Transaction API)是JavaEE 13 個開發規範之一。java 事務API,允許應用程序執行分布式事務處理——在兩個或多個網絡計算機資源上訪問並且更新數據。JDBC驅動程序的JTA支持極大地增強了數據訪問能力。事務最簡單最直接的目的就是保證數據的有效性,數據的一致性。
atomikos
實現JTA事務管理第三方管理工具 ,一個是JOTM,一個是Atomikos。在其他的博客中看到了JOTM最後更新日期是2010年,然後果斷研究是Atomikos。
Atomikos TransactionsEssentials 是一個為Java平臺提供增值服務的並且開源類事務管理器,以下是包括在這個開源版本中的一些功能:
1 <code> 全面崩潰 / 重啟恢復 2 3 兼容標準的SUN公司JTA API 4 5 嵌套事務 6 7 為XA和非XA提供內置的JDBC適配器 8 </code>
三、解決分布式事務
3.1 業務說明
業務
有兩個數據庫,分別在192.168.22.58和192.168.22.58上,分別有
t_allusers表和
t_student表,業務是要想兩個表中添加一條記錄,如果後一條失敗了,那麽前一條要回滾:
3.2 環境說明
3.2 環境說明
SSM框架
Mysql
Maven
3.3 引入依賴
3.3 引入依賴
1<!--分布式事務相關Atomikos+jta--> 2 3 <dependency> 4 5 <groupid>com.atomikos</groupid> 6 7 <artifactid>transactions-jdbc</artifactid> 8 9 <version>4.0.6</version> 10 11 </dependency> 12 13 <dependency> 14 15 <groupid>javax.transaction</groupid> 16 17 <artifactid>jta</artifactid> 18 19 <version>1.1</version> 20 21 </dependency>
3.4 配置數據源
3.4 配置數據源
在spring配置文件applicationContext-dao.xml配置文件中,添加分布式事務相關的配置:jta事務管理器,不同數據庫的數據源配置。
1 <!-- 分布式事務 --> 2 <!-- jta事務管理器 --> 3 <bean class="org.springframework.transaction.jta.JtaTransactionManager" id="jtaTransactionManager"> 4 <property name="transactionManager"> 5 <bean class="com.atomikos.icatch.jta.UserTransactionManager" destroy-method="close" init-method="init"> 6 <property name="forceShutdown" value="true"> 7 </property></bean> 8 </property> 9 <property name="userTransaction"> 10 <bean class="com.atomikos.icatch.jta.UserTransactionImp"> 11 <property name="transactionTimeout" value="300"> 12 </property></bean> 13 </property> 14 </bean> 15 <!-- 配置數據源 --> 16 <bean class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close" id="jtaDataSource1" init-method="init"> 17 <property name="uniqueResourceName" value="ds1"> 18 <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"> 19 <property name="xaProperties"> 20 <props> 21 <prop key="url">jdbc:mysql://192.168.22.58:3306/db?useUnicode=true&characterEncoding=UTF-8 22 </prop> 23 <prop key="user">root</prop> 24 <prop key="password">root</prop> 25 <prop key="pinGlobalTxToPhysicalConnection">true</prop> 26 </props> 27 </property> 28 <property name="minPoolSize" value="10"> 29 <property name="maxPoolSize" value="100"> 30 <property name="borrowConnectionTimeout" value="30"> 31 <property name="testQuery" value="select 1"> 32 <property name="maintenanceInterval" value="60"> 33 </property></property></property></property></property></property></property></bean> 34 <!-- 配置數據源 --> 35 <bean class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close" id="jtaDataSource2" init-method="init"> 36 <property name="uniqueResourceName" value="ds2"/> 37 <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/> 38 <property name="xaProperties"> 39 <props> 40 <prop key="url">jdbc:mysql://192.168.22.59:3306/db?useUnicode=true&characterEncoding=UTF-8 41 </prop> 42 <prop key="user">root</prop> 43 <prop key="password">root</prop> 44 <prop key="pinGlobalTxToPhysicalConnection">true</prop> 45 </props> 46 </property> 47 <property name="minPoolSize" value="10"/> 48 <property name="maxPoolSize" value="100"/> 49 <property name="borrowConnectionTimeout" value="30"/> 50 <property name="testQuery" value="select 1"/> 51 <property name="maintenanceInterval" value="60"/> 52 </bean>
3.5 與mybatis整合
3.5 與mybatis整合
項目開發使用ssm框架,所以還要配置spring和mybatis整合:
首先通過逆向工程,生成兩個數據庫中兩個表的mapper和實體,這裏小編把兩個數據庫生成的mapper放置到了連個不同的路徑下:
spring和mybatis結合,建立兩個sqlsessionfactory ,分別管理兩個數據庫:
1 <!-- 讓spring管理sqlsessionfactory 使用mybatis和spring整合包中的 --> 2 <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory"> 3 <!-- 數據庫連接池 --> 4 <property name="dataSource" ref="jtaDataSource1"> 5 <!-- 加載mybatis的全局配置文件 --> 6 <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"> 7 </property></property></bean> 8 9 <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory2"> 10 <!-- 數據庫連接池 --> 11 <property name="dataSource" ref="jtaDataSource2"/> 12 <!-- 加載mybatis的全局配置文件 --> 13 <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"/> 14 </bean> 15 16 <!--指定mybatis的mapper文件的位置--> 17 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 18 <property name="basePackage" value="com.dmsd.studentdao"/> 19 <property name="sqlSessionFactory" ref="sqlSessionFactory"/> 20 </bean> 21 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 22 <property name="basePackage" value="com.dmsd.dao"/> 23 <property name="sqlSessionFactory" ref="sqlSessionFactory2"/> 24 </bean>
3.6 使用Spring AOP 添加事務
3.6 使用Spring AOP 添加事務
這裏需要註意的是:通知的事務管理器是jtaTransactionManager,在前面配置Bean的時候配置的。要使用jta的 事務管理器。
1 <beans xmlns="https://www.springframework.org/schema/beans" xmlns:aop="https://www.springframework.org/schema/aop" xmlns:context="https://www.springframework.org/schema/context" xmlns:p="https://www.springframework.org/schema/p" xmlns:tx="https://www.springframework.org/schema/tx" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.2.xsd 2 https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.2.xsd 3 https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.2.xsd https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-4.2.xsd 4 https://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-4.2.xsd"> 5 <!-- 數據源 --> 6 <property name="dataSource" ref="dataSource"> 7 8 <!-- 通知 --> 9 <tx:advice id="txAdvice" transaction-manager="jtaTransactionManager"> 10 <tx:attributes> 11 <!-- 傳播行為 --> 12 <tx:method name="save*" propagation="REQUIRED"/> 13 <tx:method name="insert*" propagation="REQUIRED"/> 14 <tx:method name="add*" propagation="REQUIRED"/> 15 <tx:method name="create*" propagation="REQUIRED"/> 16 <tx:method name="delete*" propagation="REQUIRED"/> 17 <tx:method name="update*" propagation="REQUIRED"/> 18 <tx:method name="find*" propagation="SUPPORTS" read-only="true"/> 19 <tx:method name="select*" propagation="SUPPORTS" read-only="true"/> 20 <tx:method name="get*" propagation="SUPPORTS" read-only="true"/> 21 </tx:attributes> 22 </tx:advice> 23 <!-- 切面 --> 24 25 </aop:advisor></aop:config> 26 </property></beans>
3.7 實現service
3.7 實現service
1 package com.dmsd.service; 2 3 import com.dmsd.api.UserService; 4 import com.dmsd.dao.TAllusersMapper; 5 import com.dmsd.pojo.TAllusers; 6 import com.dmsd.pojo.TStudent; 7 import com.dmsd.pojo.TUser; 8 import com.dmsd.studentdao.TStudentMapper; 9 import com.sun.org.apache.bcel.internal.generic.NEW; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.beans.factory.annotation.Qualifier; 12 import org.springframework.jdbc.core.JdbcTemplate; 13 import org.springframework.stereotype.Service; 14 15 import javax.sql.DataSource; 16 import java.util.List; 17 import java.util.Map; 18 19 /** 20 * Created by Ares on 2017/10/24. 21 */ 22 @Service 23 public class UserServiceImpl implements UserService { 24 25 @Autowired 26 TAllusersMapper tAllusersMapper; 27 28 @Autowired 29 TStudentMapper tStudentMapper; 30 31 @Override 32 public void addStudent2() { 33 TAllusers tAllusers1 = tAllusersMapper.selectByPrimaryKey(1); 34 System.out.println(tAllusers1); 35 36 TStudent tStudent = new TStudent(); 37 tStudent.setAddress("langfang"); 38 tStudent.setName("AresCCCC"); 39 tStudentMapper.insert(tStudent); 40 41 TAllusers tAllusers2 =new TAllusers(); 42 tAllusers2.setAddress("shagnhai"); 43 tAllusers2.setName("AresDDDD"); 44 tAllusersMapper.insert(tAllusers2); 45 46 // int a =1/0; 47 48 } 49 }
3.8 實現Controller
3.8 實現Controller
1 package com.dmsd.controller; 2 3 import com.dmsd.api.UserService; 4 import com.dmsd.pojo.TUser; 5 import com.dmsd.tool.JacksonJsonUntil; 6 import com.fasterxml.jackson.core.JsonProcessingException; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.stereotype.Controller; 9 import org.springframework.web.bind.annotation.PathVariable; 10 import org.springframework.web.bind.annotation.RequestMapping; 11 import org.springframework.web.bind.annotation.ResponseBody; 12 13 /** 14 * Created by Ares on 2017/10/24. 15 */ 16 @Controller 17 public class UserController { 18 19 //註入api 20 @Autowired 21 private UserService userService; 22 23 @RequestMapping("/addStudent2") 24 @ResponseBody 25 public void addStudent2() { 26 userService.addStudent2(); 27 } 28 29 }
3.9 運行
3.9 運行
在瀏覽器中輸入
https://localhost:8080/addStudent2,查看數據庫:
當沒有報錯的時候,數據都插入進來:
當service中的int a =1/0;代碼解開註釋的時候,就會報錯,這樣兩個庫都插入不進去。
四、小結
四、小結
分布式事務,系統分布式後,必然會出現的技術問題。
小編就分布式事務來說,小編使用分布式事務的解決機制後,必然會造成性能的消耗。在項目建立的時候,要避免分布式事務,如果實在避免不了,可以采取下面的幾個方案:
同一個web服務器,多個數據庫,可以使用Atomikos
跨越多個web服務器的事務,如果遠程調用支持事務傳播,那麽使用JTA就可以;如果不支持事務傳播,進盡量轉化為一個web服務器的情況。
轉載:https://www.2cto.com/kf/201801/714523.html
【分布式事務】使用atomikos+jta解決分布式事務問題