【分布式事务】使用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平台提供增值服务的并且开源类事务管理器,以下是包括在这个开源版本中的一些功能:
<code> 全面崩溃 / 重启恢复 兼容标准的SUN公司JTA API 嵌套事务 为XA和非XA提供内置的JDBC适配器
</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 引入依赖<!--分布式事务相关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.4 配置数据源
3.4 配置数据源在spring配置文件applicationContext-dao.xml配置文件中,添加分布式事务相关的配置:jta事务管理器,不同数据库的数据源配置。
<!-- 分布式事务 -->
<!-- jta事务管理器 -->
<bean class="org.springframework.transaction.jta.JtaTransactionManager" id="jtaTransactionManager">
<property name="transactionManager">
<bean class="com.atomikos.icatch.jta.UserTransactionManager" destroy-method="close" init-method="init">
<property name="forceShutdown" value="true"/>
</bean>
</property>
<property name="userTransaction">
<bean class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300"/>
</bean>
</property>
</bean>
<!-- 配置数据源 -->
<bean class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close" id="jtaDataSource1" init-method="init">
<property name="uniqueResourceName" value="ds1">
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource">
<property name="xaProperties">
<props>
<prop key="url">
jdbc:mysql://192.168.22.58:3306/db?useUnicode=true&characterEncoding=UTF-8
</prop>
<prop key="user">root</prop>
<prop key="password">root</prop>
<prop key="pinGlobalTxToPhysicalConnection">true</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 class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close" id="jtaDataSource2" init-method="init">
<property name="uniqueResourceName" value="ds2"/>
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
<property name="xaProperties">
<props>
<prop key="url">
jdbc:mysql://192.168.22.59:3306/db?useUnicode=true&characterEncoding=UTF-8
</prop>
<prop key="user">root</prop>
<prop key="password">root</prop>
<prop key="pinGlobalTxToPhysicalConnection">true</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>
3.5 与mybatis整合
3.5 与mybatis整合项目开发使用ssm框架,所以还要配置spring和mybatis整合:
首先通过逆向工程,生成两个数据库中两个表的mapper和实体,这里小编把两个数据库生成的mapper放置到了连个不同的路径下:

spring和mybatis结合,建立两个sqlsessionfactory ,分别管理两个数据库:
<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<!-- 数据库连接池 -->
<property name="dataSource" ref="jtaDataSource1"/>
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"/>
</bean> <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory2">
<!-- 数据库连接池 -->
<property name="dataSource" ref="jtaDataSource2"/>
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"/>
</bean> <!--指定mybatis的mapper文件的位置-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dmsd.studentdao"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dmsd.dao"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory2"/>
</bean>
3.6 使用Spring AOP 添加事务
3.6 使用Spring AOP 添加事务这里需要注意的是:通知的事务管理器是jtaTransactionManager,在前面配置Bean的时候配置的。要使用jta的 事务管理器。
<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
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-4.2.xsd
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
https://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util-4.2.xsd">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"> <!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="jtaTransactionManager">
<tx:attributes>
<!-- 传播行为 -->
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="create*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--自动为切面方法中匹配的方法所在的类生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
3.7 实现service
3.7 实现servicepackage com.dmsd.service; import com.dmsd.api.UserService;
import com.dmsd.dao.TAllusersMapper;
import com.dmsd.pojo.TAllusers;
import com.dmsd.pojo.TStudent;
import com.dmsd.pojo.TUser;
import com.dmsd.studentdao.TStudentMapper;
import com.sun.org.apache.bcel.internal.generic.NEW;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service; import javax.sql.DataSource;
import java.util.List;
import java.util.Map; /**
* Created by Ares on 2017/10/24.
*/
@Service
public class UserServiceImpl implements UserService { @Autowired
TAllusersMapper tAllusersMapper; @Autowired
TStudentMapper tStudentMapper; @Override
public void addStudent2() {
TAllusers tAllusers1 = tAllusersMapper.selectByPrimaryKey(1);
System.out.println(tAllusers1); TStudent tStudent = new TStudent();
tStudent.setAddress("langfang");
tStudent.setName("AresCCCC");
tStudentMapper.insert(tStudent); TAllusers tAllusers2 =new TAllusers();
tAllusers2.setAddress("shagnhai");
tAllusers2.setName("AresDDDD");
tAllusersMapper.insert(tAllusers2); // int a =1/0; }
}
3.8 实现Controller
3.8 实现Controller1 package com.dmsd.controller; import com.dmsd.api.UserService;
import com.dmsd.pojo.TUser;
import com.dmsd.tool.JacksonJsonUntil;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; /**
* Created by Ares on 2017/10/24.
*/
@Controller
public class UserController { //注入api
@Autowired
private UserService userService; @RequestMapping("/addStudent2")
@ResponseBody
public void addStudent2() {
userService.addStudent2();
} }
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解决分布式事务问题的更多相关文章
- Spring3.0+Hibernate+Atomikos集成构建JTA的分布式事务--解决多数据源跨库事务
一.概念 分布式事务分布式事务是指事务的参与者.支持事务的服务器.资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上.简言之,同时操作多个数据库保持事务的统一,达到跨库事务的效果. JTA ...
- spring3.0+Atomikos 构建jta的分布式事务 -- NO
摘自: http://gongjiayun.iteye.com/blog/1570111 spring3.0+Atomikos 构建jta的分布式事务 spring3.0已经不再支持jtom了,不过我 ...
- spring3.0+Atomikos 构建jta的分布式事务
摘自: http://gongjiayun.iteye.com/blog/1570111 spring3.0+Atomikos 构建jta的分布式事务 spring3.0已经不再支持jtom了,不过我 ...
- SpringMVC+MyBatis+JMS+JTA(分布式事务)
SpringMVC+MyBatis 相信已经是如今企业开发中经常使用技术了. 由于一些需求,我们须要集成JMS(我使用的是ActiveMQ).大家应该都知道.MQ也能够觉得是一个数据源.数据也是数据源 ...
- LCN解决分布式事务原理解析+项目实战(原创精华版)
写在前面: 原创不易,如果觉得不错推荐一下,谢谢! 由于工作需要,公司的微服务项目需解决分布式事务的问题,且由我进行分布式事务框架搭建和整合工作. 那么借此机会好好的将解决分布式事务的内容进行整理一下 ...
- 解决分布式事务基本思想Base和CPA理论、最终一致性|刚性事务、柔性事务
在学习解决分布式事务基本思路之前,大家要熟悉一些基本解决分布式事务概念名词比如:CAP与Base理论.柔性事务与刚性事务.理解最终一致性思想,JTA+XA.两阶段与三阶段提交等. 如何保证强一致性呢? ...
- springboot整合多数据源解决分布式事务
一.前言 springboot整合多数据源解决分布式事务. 1.多数据源采用分包策略 2.全局分布式事务管理:jta-atomikos. ...
- 一文教你迅速解决分布式事务 XA 一致性问题
欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯云数据库团队 近日,腾讯云发布了分布式数据库解决方案(DCDB),其最明显的特性之一就是提供了高于开源分布式事务XA的性能.大型 ...
- 分布式消息队列RocketMQ--事务消息--解决分布式事务
说到分布式事务,就会谈到那个经典的”账号转账”问题:2个账号,分布处于2个不同的DB,或者说2个不同的子系统里面,A要扣钱,B要加钱,如何保证原子性? 一般的思路都是通过消息中间件来实现“最终一致性” ...
随机推荐
- OSI七层协议和TCP/IP四层协议
1. OSI七层和TCP/IP四层的关系 1.1 OSI引入了服务.接口.协议.分层的概念,TCP/IP借鉴了OSI的这些概念建立TCP/IP模型. 1.2 OSI先有模型,后有协议,先有标准,后进行 ...
- 对称加密实现重要日志上报Openresty接口服务
记录后端接收日志的流程: 由于记录的是广告数据,单次计费数据都会上报,全国内约10几万终端上报. 终端上报:Android电视端Apk上报 接收终端:Openresty(Nginx+lua)利用ngi ...
- docker 在push镜像到本地registry出现的500 Internal Server Error
]# docker push 192.168.163.131:5000/test The push refers to a repository [192.168.163.131:5000/test] ...
- js实现QQ跳转到支付宝APP并领取红包!附:动图demo
前天我在sg开源了js实现微信跳转到支付宝并领红包的代码.https://segmentfault.com/a/11...于是朋友圈开始刷屏,各种套路,各种标题,再附上短链接,引起了很多人的好奇,然后 ...
- 埋点(Event Tracking)vs 无埋点(Codeless Tracking) vs 可视化埋点(Visual Event Tracking)
在理解什么是埋点之前,首先需要了解一些基础知识:(以下摘自:http://www.chinawebanalytics.cn/auto-event-tracking-good-bad-ugly/) 我们 ...
- qt 启动参数 -qws
运行嵌入式程序 在嵌入式QT版本中,程序需要服务器或自己作为服务器程序. 服务器程序构造的方法是构造一个QApplication::GuiServe类型的QApplication对象.或者使用-qws ...
- PHP mysqli_multi_query() 函数
实例 执行多个针对数据库的查询: <?php $con=mysqli_connect("localhost","my_user","my_pas ...
- HDU 5527 Too Rich ( 15长春区域赛 A 、可贪心的凑硬币问题 )
题目链接 题意 : 给出一些固定面值的硬币的数量.再给你一个总金额.问你最多能用多少硬币来刚好凑够这个金额.硬币数量和总金额都很大 分析 : 长春赛区的金牌题目 一开始认为除了做类似背包DP那样子 ...
- useradd/usermod/userdel/passwd/groupadd/groupmod/groupdel/gpasswd
用户 用户系统也是通过一个文件来管理的,默认的root用户id是0, shadow文件说明 加密算法类别 $后面的数字6指定了加密算法使用的是第六种,sha512加密 增加用户,修改成同样的密码,查看 ...
- Poj 3764 The xor-longest Path(Trie树+xor+贪心)
The xor-longest Path Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 6455 Accepted: 1392 ...