一、前言

分布式事务,这个问题困惑了小编很久,在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 环境说明

SSM框架

Mysql

Maven

          

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

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

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

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

 package 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

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

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

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

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

四、小结

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

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

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

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

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

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

  1. Spring3.0+Hibernate+Atomikos集成构建JTA的分布式事务--解决多数据源跨库事务

    一.概念 分布式事务分布式事务是指事务的参与者.支持事务的服务器.资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上.简言之,同时操作多个数据库保持事务的统一,达到跨库事务的效果. JTA ...

  2. spring3.0+Atomikos 构建jta的分布式事务 -- NO

    摘自: http://gongjiayun.iteye.com/blog/1570111 spring3.0+Atomikos 构建jta的分布式事务 spring3.0已经不再支持jtom了,不过我 ...

  3. spring3.0+Atomikos 构建jta的分布式事务

    摘自: http://gongjiayun.iteye.com/blog/1570111 spring3.0+Atomikos 构建jta的分布式事务 spring3.0已经不再支持jtom了,不过我 ...

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

    SpringMVC+MyBatis 相信已经是如今企业开发中经常使用技术了. 由于一些需求,我们须要集成JMS(我使用的是ActiveMQ).大家应该都知道.MQ也能够觉得是一个数据源.数据也是数据源 ...

  5. LCN解决分布式事务原理解析+项目实战(原创精华版)

    写在前面: 原创不易,如果觉得不错推荐一下,谢谢! 由于工作需要,公司的微服务项目需解决分布式事务的问题,且由我进行分布式事务框架搭建和整合工作. 那么借此机会好好的将解决分布式事务的内容进行整理一下 ...

  6. 解决分布式事务基本思想Base和CPA理论、最终一致性|刚性事务、柔性事务

    在学习解决分布式事务基本思路之前,大家要熟悉一些基本解决分布式事务概念名词比如:CAP与Base理论.柔性事务与刚性事务.理解最终一致性思想,JTA+XA.两阶段与三阶段提交等. 如何保证强一致性呢? ...

  7. springboot整合多数据源解决分布式事务

    一.前言        springboot整合多数据源解决分布式事务.             1.多数据源采用分包策略              2.全局分布式事务管理:jta-atomikos. ...

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

    欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯云数据库团队 近日,腾讯云发布了分布式数据库解决方案(DCDB),其最明显的特性之一就是提供了高于开源分布式事务XA的性能.大型 ...

  9. 分布式消息队列RocketMQ--事务消息--解决分布式事务

    说到分布式事务,就会谈到那个经典的”账号转账”问题:2个账号,分布处于2个不同的DB,或者说2个不同的子系统里面,A要扣钱,B要加钱,如何保证原子性? 一般的思路都是通过消息中间件来实现“最终一致性” ...

随机推荐

  1. [Abp vNext微服务实践] - 添加中文语言

    简介 abp vNext中提供了多语言功能,默认语言是英文,没有提供中文语言包.在业务开发中,定义权限后需要用中文的备注提供角色选择,本篇将介绍如何在abp vNext中加入中文语言. step1:添 ...

  2. idou老师教你学istio1:如何为服务提供安全防护能力

    之前,已为大家介绍过 Istio 第一主打功能---连接服务. 凡是产生连接关系,就必定带来安全问题,人类社会如此,服务网格世界,亦是如此. 今天,我们就来谈谈Istio第二主打功能---保护服务. ...

  3. a 标签的download 属性在谷歌浏览器下无法下载图片,如何处理?

    a 标签的download属性在下载图片文件的时候是如何的方便,可是可是谷歌浏览器不支持下载,而是下载打开新窗口预览图片.这个兼容性问题如何解决呢? 了解了一番,HTMLCanvasElement.t ...

  4. 【WEB】jQuery 判断复选框是否选中

    1.背景 在 jQuery 1.6 版本之前,判断方式 <input type='checkbox' id='test'/> <script> var isChecked = ...

  5. struts2之单文件上传(7)

    前台页面jsp <!-- 拦截的时候用这个 <s:form action="uploadAction" enctype="multipart/form-dat ...

  6. Qt 程序自动重启的实现

    正常退出调用exit() 或quit()就行,想要自已重启可按下面代码: void XXX:onRestart() { //类中调用 qApp->exit(); } 主main函数中处理 int ...

  7. More development resources

    社区 名称 官网 google https://www.google.com/ github https://github.com/ StackOverflow https://stackoverfl ...

  8. Laravel 项目中事件控制的体会--综合应用 trait 多态

    1 我们想像有这样的需求 1.1 应用中有两个类.其一是 荣誉(Honour)其一是 档案(Archive)Honour 和 Arhcive 是多态关联.即拥有档案属性的不只荣誉类,还有更多的类去关联 ...

  9. 路由器配置——RIP路由

    一.实验目的:用rip路由实现全网互通 二.拓扑图: 三.具体步骤配置 (1)R1路由器配置 Router>enable  --进入特权模式Router#configure terminal   ...

  10. tqdm如何在pandas里面使用

    原文: https://segmentfault.com/a/1190000016059726 当然,首先我们得载入模块,在notebook中使用tqdm带的基于Js显示的进度条前,请务必检查是否安装 ...