1. 程式人生 > >【分散式事務】使用atomikos+jta解決分散式事務問題

【分散式事務】使用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.1 環境說明

SSM框架

Mysql

Maven

           這裡寫圖片描述

 

3.2 引入依賴

<!--分散式事務相關Atomikos+jta-->

<dependency>

    <groupid>com.atomikos</groupid>

    <artifactid>transactions-jdbc</artifactid>

    <version>4.0.6</version>

</dependency>

<dependency>

   <groupid>javax.transaction</groupid>

   <artifactid>jta</artifactid>

   <version>1.1</version>

</dependency>

 

3.3 配置資料來源

在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.4 與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.5 使用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.6 實現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.7 實現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.8 執行

瀏覽器中輸入https://localhost:8080/addStudent2,檢視資料庫:

當沒有報錯的時候,資料都插入進來:

 


這裡寫圖片描述

 

 



 這裡寫圖片描述

當service中的int a =1/0;程式碼解開註釋的時候,就會報錯,這樣兩個庫都插入不進去。

四、小結

分散式事務,系統分散式後,必然會出現的技術問題。

小編就分散式事務來說,小編使用分散式事務的解決機制後,必然會造成效能的消耗。在專案建立的時候,要避免分散式事務,如果實在避免不了,可以採取下面的幾個方案:

同一個web伺服器,多個數據庫,可以使用Atomikos

跨越多個web伺服器的事務,如果遠端呼叫支援事務傳播,那麼使用JTA就可以;如果不支援事務傳播,進儘量轉化為一個web伺服器的情況。

 

 

 

 

轉載:https://www.2cto.com/kf/201801/714523.html