一、前言

分布式事务,这个问题困惑了小编很久,在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. C++STL库常用函数用法

    开学就要上OOP了.....感觉十分萌萌哒- -! 整理自<ACM程序设计>,本文为转载(原文地址) 迭代器(iterator) 个人理解就是把所有和迭代有关的东西给抽象出来的,不管是数组 ...

  2. Windows&Appium&Python自动化测试-环境搭建之安卓SDK

    一.摘要 本博文将详细讲述在Windows环境下的jdk安装.配置以及环境校验:安卓sdk安装.配置以及环境校验 二.安装包工具准备: jdk1.8.0(64 位) installer_r24.4.1 ...

  3. Python 获得程序 exe 的版本号

    Python 获得程序 exe 的版本号 python中需要安装 pywin32 包 # based on http://stackoverflow.com/questions/580924/pyth ...

  4. GNU Radio下QT GUI Tab Widget的使用方法

    期望显示出的效果: 即将要显示的图放在各自的标签页中. 整体框图: 具体设置: QT GUI Tab Widget的设置: 其中 ID改为自己想改的,这里我写的是display GUI Hint所代表 ...

  5. CF311B Cats Transport(斜率优化)

    题目描述 Zxr960115 是一个大农场主.他养了m只可爱的猫子,雇佣了p个铲屎官.这里有一条又直又长的道路穿过了农场,有n个山丘坐落在道路周围,编号自左往右从1到n.山丘i与山丘i-1的距离是Di ...

  6. [React] Reduce Code Redundancy with Custom React Hooks

    In this lesson, we'll cover how to create a custom React hook for managing the state of any input. T ...

  7. 小米oj 重拍数组求最大和

     重排数组求最大和 序号:#34难度:困难时间限制:1000ms内存限制:10M 描述 假设有一个n元素的数组(数组的元素索引从1开始),针对这个数组有q个查询请求,每个请求由一对整数li,ri组成, ...

  8. 【csp模拟赛3】组合数学

    思路: 先排序,取最大的在剩余左边任意找k-1个数,所以是排列组合,费马小定理求逆元,预处理阶乘,注意要取模.. 代码: #include<cstdio> #include<iost ...

  9. CoreText学习(二)之Hello world

    最后更新:2017-08-10 部分内容丢失,后续补上 相关配置: Xcode 8.3.3 Swift 3.0 macOS Sierra 一.CoreText 简介 CoreText 是用于处理文字和 ...

  10. Java中二维数组

    二维数组:(其实是一个一维数组,它的每一个元素又是一个一维数组), 可以看做是一张表格. 初始化: 动态初始化 int[ ][ ]  arr = new int[3][2]; 定义了一个二维数组,其中 ...