【分布式事务】使用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 环境说明

SSM框架

Mysql

Maven

          

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 配置数据源

在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整合

项目开发使用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 添加事务

这里需要注意的是:通知的事务管理器是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

 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

 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 运行

浏览器中输入https://localhost:8080/addStudent2,查看数据库:

当没有报错的时候,数据都插入进来:

当service中的int a =1/0;代码解开注释的时候,就会报错,这样两个库都插入不进去。

四、小结

分布式事务,系统分布式后,必然会出现的技术问题。

小编就分布式事务来说,小编使用分布式事务的解决机制后,必然会造成性能的消耗。在项目建立的时候,要避免分布式事务,如果实在避免不了,可以采取下面的几个方案:

同一个web服务器,多个数据库,可以使用Atomikos

跨越多个web服务器的事务,如果远程调用支持事务传播,那么使用JTA就可以;如果不支持事务传播,进尽量转化为一个web服务器的情况。

转载:https://www.2cto.com/kf/201801/714523.html

原文地址:https://www.cnblogs.com/ynyhl/p/10062527.html

时间: 2024-07-30 10:13:13

【分布式事务】使用atomikos+jta解决分布式事务问题的相关文章

基于spring+mybatis+atomikos+jta实现分布式事务(2)-动态切换数据源

本文介绍基于spring+mybatis+atomikos+jta实现分布式事务,由程序动态切换数据源,通过atomikos可实现分布式事务一致性. 版本:spring-3.2.9.RELEASE.mybatis-3.4.4.atomikos-4.0.5.jdk1.8 1,maven配置文件pom.xml如下: <!-- test --> <dependency> <groupId>junit</groupId> <artifactId>juni

SpringMVC+MyBatis+JMS+JTA(分布式事务)

SpringMVC+MyBatis 相信已经是现在企业开发中常用技术了. 因为一些需求,我们需要集成JMS(我使用的是ActiveMQ),大家应该都知道,MQ也可以认为是一个数据源,数据也是数据源.这种情况下,如果我们在一个方法内操作JMS和数据库,我们就需要保证这个方法执行需要满足原子性. 这也就意味这一个问题,我们要多个数据源在同一个事务中.这里不枚举市面上的所有解决方案,其实atomikos JTA 是一个比较不错分布式事务管理器. 当然如果没有使用到JMS,在需要多数据源(也就是需要连接

使用kafka消息队列解决分布式事务

微服务框架Spring Cloud介绍 Part1: 使用事件和消息队列实现分布式事务 本文转自:http://skaka.me/blog/2016/04/21/springcloud1/ 不同于单一架构应用(Monolith), 分布式环境下, 进行事务操作将变得困难, 因为分布式环境通常会有多个数据源, 只用本地数据库事务难以保证多个数据源数据的一致性. 这种情况下, 可以使用两阶段或者三阶段提交协议来完成分布式事务.但是使用这种方式一般来说性能较差, 因为事务管理器需要在多个数据源之间进行

一文教你迅速解决分布式事务 XA 一致性问题

欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯云数据库团队 近日,腾讯云发布了分布式数据库解决方案(DCDB),其最明显的特性之一就是提供了高于开源分布式事务XA的性能.大型业务系统有着用户多.并发高的特点,在这方面,集中式数据库(单机数据库)的性能很难支持,因此主流的互联网公司往往采用分布式(架构)数据库,物理上利用更多的低端设备,逻辑上对大表水平拆分支撑业务的需要. 虽然分布式数据库能解决性能难题,但事务一致性(Consistency)的问题,却很难在分布式数据库上

RabbitMQ解决分布式事务

案例:经典案例,以目前流行点外卖的案例,用户下单后,调用订单服务,让后订单服务调用派单系统通知送外卖人员送单,这时候订单系统与派单系统采用MQ异步通讯. RabbitMQ解决分布式事务原理: 采用最终一致性原理.需要保证以下三要素1.确认生产者一定要将数据投递到MQ服务器中(采用MQ消息确认机制)2.MQ消费者消息能够正确消费消息,采用手动ACK模式,使用不补偿机制(注意重试幂等性问题)3.如何保证第一个事务先执行,采用补偿机制(补单机制),在创建一个补单消费者进行监听,如果订单没有创建成功,进

搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务

搞懂分布式技术19:使用RocketMQ事务消息解决分布式事务 初步认识RocketMQ的核心模块 rocketmq模块 rocketmq-broker:接受生产者发来的消息并存储(通过调用rocketmq-store),消费者从这里取得消息. rocketmq-client:提供发送.接受消息的客户端API. rocketmq-namesrv:NameServer,类似于Zookeeper,这里保存着消息的TopicName,队列等运行时的元信息.(有点NameNode的味道) rocketm

使用spring+hibernate+atomikos+tomcat构建分布式事务

本文通过一个demo,介绍如何使用spring+hibernate+atomikos+tomcat构建在一个事务中涉及两个数据源的web应用. demo功能:实现一个能成功提交和回滚的涉及两个数据库数据源的XA事务. demo将实现: 1.一次性在两个数据库的两张表中各插入一条数据并提交. 2.一次性在两个数据库的两张表中各插入一条数据并回滚. 测试方式:restful web api 使用工具: spring 4.1.1.RELEASE hibernate 4.2.4.Final atomik

使用spring+mybatis+atomikos+tomcat构建分布式事务

本文通过一个demo,介绍如何使用spring+mybatis+atomikos+tomcat构建在一个事务中涉及两个数据源的web应用. demo功能:实现一个能成功提交和回滚的涉及两个数据库数据源的XA事务. demo将实现: 1.一次性在两个数据库的两张表中各插入一条数据并提交. 2.一次性在两个数据库的两张表中各插入一条数据并回滚. 测试方式:restful web api 使用工具: spring 4.1.1.RELEASE mybatis 3.2.7 atomikos 3.7.0 t

Spring Boot微服务如何集成fescar解决分布式事务?

什么是fescar? 关于fescar的详细介绍,请参阅fescar wiki. 传统的2PC提交协议,会持有一个全局性的锁,所有局部事务预提交成功后一起提交,或有一个局部事务预提交失败后一起回滚,最后释放全局锁.锁持有的时间较长,会对并发造成较大的影响,死锁的风险也较高. fescar的创新之处在于,每个局部事务执行完立即提交,释放本地锁:它会去解析你代码中的sql,从数据库中获得事务提交前的事务资源即数据,存放到undo_log中,全局事务协调器在回滚的时候直接使用undo_log中的数据覆