经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 软件/图像 » Atom » 查看文章
Spring中使用atomikos+druid实现经典分布式事务的方法
来源:jb51  时间:2019/6/19 9:54:22  对本文有异议

经典分布式事务,是相对互联网中的柔性分布式事务而言,其特性为ACID原则,包括原子性(Atomictiy)、一致性(Consistency)、隔离性(Isolation)、持久性(Durabilit):

  • 原子性:事务是一个包含一系列操作的原子操作。事务的原子性确保这些操作全部完成或者全部失败。
  • 一致性:一旦事务的所有操作结束,事务就被提交。然后你的数据和资源将处于遵循业务规则的一直状态。
  • 隔离性:因为同时在相同数据集上可能有许多事务处理,每个事务应该与其他事务隔离,避免数据破坏。
  • 持久性:一旦事务完成,他的结果应该能够承受任何系统错误(想象一下在事务提交过程中机器的电源被切断的情况)。通常,事务的结果被写入持续性存储。

XA是啥?

XA是由X/Open组织提出的分布式事务的架构(或者叫协议)。XA架构主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。也就是说,在基于XA的一个事务中,我们可以针对多个资源进行事务管理,例如一个系统访问多个数据库,或即访问数据库、又访问像消息中间件这样的资源。这样我们就能够实现在多个数据库和消息中间件直接实现全部提交、或全部取消的事务。XA规范不是java的规范,而是一种通用的规范,

目前各种数据库、以及很多消息中间件都支持XA规范。

JTA是满足XA规范的、用于Java开发的规范。所以,当我们说,使用JTA实现分布式事务的时候,其实就是说,使用JTA规范,实现系统内多个数据库、消息中间件等资源的事务。

JTA(Java Transaction API),是J2EE的编程接口规范,它是XA协议的JAVA实现。它主要定义了:

  • 一个事务管理器的接口javax.transaction.TransactionManager,定义了有关事务的开始、提交、撤回等>操作。
  • 一个满足XA规范的资源定义接口javax.transaction.xa.XAResource,一种资源如果要支持JTA事务,就需要让它的资源实现该XAResource接口,并实现该接口定义的两阶段提交相关的接口。如果我们有一个应用,它使用JTA接口实现事务,应用在运行的时候,就需要一个实现JTA的容器,一般情况下,这是一个J2EE容器,像JBoss,Websphere等应用服务器。但是,也有一些独立的框架实现了JTA,例如 Atomikos, bitronix 都提供了jar包方式的JTA实现框架。这样我们就能够在Tomcat或者Jetty之类的服务器上运行使用JTA实现事务的应用系统。在上面的本地事务和外部事务的区别中说到,JTA事务是外部事务,可以用来实现对多个资源的事务性。它正是通过每个资源实现的XAResource来进行两阶段提交的控制。感兴趣的同学可以看看这个接口的方法,除了commit, rollback等方法以外,还有end(), forget(), isSameRM(), prepare()等等。光从这些接口就能够想象JTA在实现两阶段事务的复杂性。

本篇以Spring MVC+Maven+Atomikos+Druid+MyBatis演示分布式事务的实现。

Mave 的pom.xml

  1. <properties>
  2. <jdk.version>1.8</jdk.version>
  3. <!-- 注mysql的版本和druid的版本一定要搭配,否则会有问题,目前这两个版本是搭配好的 -->
  4. <mysql.version>8.0.11</mysql.version>
  5. <druid.version>1.1.17</druid.version>
  6. <spring.version>5.1.8.RELEASE</spring.version>
  7. <cglib.version>3.2.12</cglib.version>
  8. <atomikos.version>5.0.0</atomikos.version>
  9. <aspectjweaver.version>1.9.4</aspectjweaver.version>
  10. <aspectjrt.version>1.5.4</aspectjrt.version>
  11. <jta.version>1.1</jta.version>
  12. <mybatise.version>3.2.0</mybatise.version>
  13. <mybatis.spring>1.2.0</mybatis.spring>
  14. <log4j.version>1.2.17</log4j.version>
  15. <junit.version>4.12</junit.version>
  16. <cglib.version>3.2.4</cglib.version>
  17. </properties>
  18. <dependencies>
  19. <dependency>
  20. <groupId>org.mybatis</groupId>
  21. <artifactId>mybatis</artifactId>
  22. <version>${mybatise.version}</version>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.mybatis</groupId>
  26. <artifactId>mybatis-spring</artifactId>
  27. <version>${mybatis.spring}</version>
  28. </dependency>
  29. <dependency>
  30. <groupId>com.atomikos</groupId>
  31. <artifactId>atomikos-util</artifactId>
  32. <version>${atomikos.version}</version>
  33. </dependency>
  34. <dependency>
  35. <groupId>com.atomikos</groupId>
  36. <artifactId>transactions</artifactId>
  37. <version>${atomikos.version}</version>
  38. </dependency>
  39. <dependency>
  40. <groupId>com.atomikos</groupId>
  41. <artifactId>transactions-jta</artifactId>
  42. <version>${atomikos.version}</version>
  43. </dependency>
  44. <dependency>
  45. <groupId>com.atomikos</groupId>
  46. <artifactId>transactions-jdbc</artifactId>
  47. <version>${atomikos.version}</version>
  48. </dependency>
  49. <dependency>
  50. <groupId>com.atomikos</groupId>
  51. <artifactId>transactions-api</artifactId>
  52. <version>${atomikos.version}</version>
  53. </dependency>
  54. <dependency>
  55. <groupId>javax.transaction</groupId>
  56. <artifactId>jta</artifactId>
  57. <version>${jta.version}</version>
  58. </dependency>
  59. <dependency>
  60. <groupId>cglib</groupId>
  61. <artifactId>cglib-nodep</artifactId>
  62. <version>${cglib.version}</version>
  63. </dependency>
  64. <dependency>
  65. <groupId>org.springframework</groupId>
  66. <artifactId>spring-test</artifactId>
  67. <version>${spring.version}</version>
  68. </dependency>
  69. <dependency>
  70. <groupId>org.springframework</groupId>
  71. <artifactId>spring-web</artifactId>
  72. <version>${spring.version}</version>
  73. </dependency>
  74. <dependency>
  75. <groupId>org.springframework</groupId>
  76. <artifactId>spring-tx</artifactId>
  77. <version>${spring.version}</version>
  78. </dependency>
  79. <dependency>
  80. <groupId>org.springframework</groupId>
  81. <artifactId>spring-beans</artifactId>
  82. <version>${spring.version}</version>
  83. </dependency>
  84. <dependency>
  85. <groupId>org.springframework</groupId>
  86. <artifactId>spring-jdbc</artifactId>
  87. <version>${spring.version}</version>
  88. </dependency>
  89. <dependency>
  90. <groupId>org.springframework</groupId>
  91. <artifactId>spring-webmvc</artifactId>
  92. <version>${spring.version}</version>
  93. </dependency>
  94. <dependency>
  95. <groupId>org.springframework</groupId>
  96. <artifactId>spring-orm</artifactId>
  97. <version>${spring.version}</version>
  98. </dependency>
  99. <dependency>
  100. <groupId>org.springframework</groupId>
  101. <artifactId>spring-context-support</artifactId>
  102. <version>${spring.version}</version>
  103. </dependency>
  104. <!-- Spring Aop依赖jar -->
  105. <dependency>
  106. <groupId>org.aspectj</groupId>
  107. <artifactId>aspectjweaver</artifactId>
  108. <version>${aspectjweaver.version}</version>
  109. </dependency>
  110.  
  111. <dependency>
  112. <groupId>aspectj</groupId>
  113. <artifactId>aspectjrt</artifactId>
  114. <version>${aspectjrt.version}</version>
  115. </dependency>
  116. <!-- CGLIB -->
  117. <dependency>
  118. <groupId>cglib</groupId>
  119. <artifactId>cglib</artifactId>
  120. <version>${cglib.version}</version>
  121. </dependency>
  122. <dependency>
  123. <groupId>mysql</groupId>
  124. <artifactId>mysql-connector-java</artifactId>
  125. <version>${mysql.version}</version>
  126. </dependency>
  127. <dependency>
  128. <groupId>com.alibaba</groupId>
  129. <artifactId>druid</artifactId>
  130. <version>${druid.version}</version>
  131. </dependency>
  132. <dependency>
  133. <groupId>junit</groupId>
  134. <artifactId>junit</artifactId>
  135. <version>${junit.version}</version>
  136. <scopte>test</scope>
  137. </dependency>
  138. </dependencies>
  139.  

spring-application-context.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xmlns:context="http://www.springframework.org/schema/context"
  7. xmlns:aop="http://www.springframework.org/schema/aop"
  8. xmlns:mvc="http://www.springframework.org/schema/mvc"
  9. xmlns:task="http://www.springframework.org/schema/task"
  10. xsi:schemaLocation="http://www.springframework.org/schema/beans
  11. http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  12. http://www.springframework.org/schema/mvc
  13. http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
  14. http://www.springframework.org/schema/context
  15. http://www.springframework.org/schema/context/spring-context-3.1.xsd
  16. http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
  17. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
  18. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
  19. <!-- 1. 数据源配置 -->
  20. <context:property-placeholder location="classpath*:*.properties" file-encoding="utf8" />
  21. <bean id="utf8" class="java.lang.String">
  22. <constructor-arg value="utf-8"></constructor-arg>
  23. </bean>
  24. <!-- 开启异步任务(同时开启定时器注解扫描) -->
  25. <task:annotation-driven />
  26. <!-- 使用@AspectJ风格的切面声明 -->
  27. <!-- <aop:aspectj-autoproxy/> -->
  28. <!-- 使用Annotation自动注册Bean -->
  29. <!-- 在主容器中不扫描@Controller注解,在SpringMvc中只扫描@Controller注解 -->
  30. <context:component-scan base-package="net.xiake6"><!-- base-package 如果多个,用“,”分隔 -->
  31. <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
  32. </context:component-scan>
  33. <!-- 引入Mybatis配置 -->
  34. <!-- <import resource="spring-mybatis-atomikos-druid.xml"/> -->
  35. <import resource="spring-mybatis-atomikos-druid.xml"/>
  36. </beans>

spring-mybatis-atomikos-druid.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:jee="http://www.springframework.org/schema/jee"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xmlns:context="http://www.springframework.org/schema/context"
  7. xmlns:aop="http://www.springframework.org/schema/aop"
  8. xsi:schemaLocation="http://www.springframework.org/schema/beans
  9. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  10. http://www.springframework.org/schema/tx
  11. http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
  12. http://www.springframework.org/schema/jee
  13. http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
  14. http://www.springframework.org/schema/aop
  15. http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
  16. http://www.springframework.org/schema/context
  17. http://www.springframework.org/schema/context/spring-context-3.0.xsd"
  18. default-lazy-init="true">
  19.  
  20. <context:annotation-config />
  21. <!-- 使用Druid使为XA数据源 -->
  22. <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true">
  23. <property name="xaDataSourceClassName" value="com.alibaba.druid.pool.xa.DruidXADataSource"/>
  24. <property name="xaProperties">
  25. <props>
  26. <prop key="driverClassName">${jdbc.driverClassName}</prop>
  27. <!-- 配置初始化大小、最小、最大 -->
  28. <prop key="initialSize">10</prop>
  29. <prop key="minIdle">3</prop>
  30. <prop key="maxActive">100</prop>
  31. <!-- 配置获取连接等待超时的时间 -->
  32. <prop key="maxWait">60000</prop>
  33. <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
  34. <prop key="timeBetweenEvictionRunsMillis">60000</prop>
  35. <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
  36. <prop key="minEvictableIdleTimeMillis">300000</prop>
  37. <prop key="validationQuery">SELECT 'x'</prop>
  38. <prop key="testWhileIdle">true</prop>
  39. <prop key="testOnBorrow">false</prop>
  40. <prop key="testOnReturn">false</prop>
  41. <!-- 配置监控统计拦截的filters -->
  42. <prop key="filters">stat</prop>
  43. </props>
  44. </property>
  45. </bean>
  46. <!-- 配置数据源一 -->
  47. <bean id="dataSourceOne" parent="abstractXADataSource">
  48. <property name="uniqueResourceName">
  49. <value>dataSourceOne</value>
  50. </property>
  51. <property name="xaProperties">
  52. <props>
  53. <prop key="url">${jdbc.url}</prop>
  54. <prop key="username">${jdbc.username}</prop>
  55. <prop key="password">${jdbc.password}</prop>
  56. </props>
  57. </property>
  58. </bean>
  59. <!--配置数据源二-->
  60. <bean id="dataSourceTwo" parent="abstractXADataSource">
  61. <property name="uniqueResourceName">
  62. <value>dataSourceTwo</value>
  63. </property>
  64. <property name="xaProperties">
  65. <props>
  66. <prop key="url">${jdbc.two.url}</prop>
  67. <prop key="username">${jdbc.two.username}</prop>
  68. <prop key="password">${jdbc.two.password}</prop>
  69. </props>
  70. </property>
  71. </bean>
  72. <!--mybatis的相关配置-->
  73. <bean id="sqlSessionFactoryOne" class="org.mybatis.spring.SqlSessionFactoryBean">
  74. <property name="dataSource" ref="dataSourceOne"/>
  75. <property name="mapperLocations" value="classpath*:mapping/ds1/*.xml"/>
  76. </bean>
  77. <bean id="sqlSessionFactoryTwo" class="org.mybatis.spring.SqlSessionFactoryBean">
  78. <property name="dataSource" ref="dataSourceTwo"/>
  79. <property name="mapperLocations" value="classpath*:mapping/ds2/*.xml"/>
  80. </bean>
  81. <!--配置mybatis映射文件自动扫描-->
  82. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  83. <property name="basePackage" value="net.xiake6.dao.ds1"/>
  84. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryOne"/>
  85. </bean>
  86.  
  87. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  88. <property name="basePackage" value="net.xiake6.dao.ds2"/>
  89. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryTwo"/>
  90. </bean>
  91.  
  92. <!--配置分布式事务-->
  93. <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
  94. <property name="forceShutdown" value="false"/>
  95. </bean>
  96. <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
  97. <property name="transactionTimeout" value="3000"/>
  98. </bean>
  99. <!--JTA事务管理器-->
  100. <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
  101. <property name="transactionManager">
  102. <ref bean="atomikosTransactionManager"/>
  103. </property>
  104. <property name="userTransaction">
  105. <ref bean="atomikosUserTransaction"/>
  106. </property>
  107. <property name="allowCustomIsolationLevels" value="true"/>
  108. </bean>
  109. <aop:config proxy-target-class="true">
  110. <aop:advisor pointcut="execution(* *net.xiake6.service..*(..))" advice-ref="txAdvice" />
  111. </aop:config>
  112. <tx:advice id="txAdvice" transaction-manager="jtaTransactionManager">
  113. <tx:attributes>
  114. <tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="*Exception"/>
  115. <tx:method name="add*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
  116. <tx:method name="save*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
  117. <tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
  118. <tx:method name="del*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
  119. <tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="*Exception" />
  120. <tx:method name="select*" propagation="REQUIRED" read-only="true" />
  121. <tx:method name="query" propagation="REQUIRED" read-only="true" />
  122. </tx:attributes>
  123. </tx:advice>
  124. <!-- 配置事务管理 -->
  125. <tx:annotation-driven transaction-manager="jtaTransactionManager" />
  126. </beans>
  127.  

jdbc.properties

  1. #mysql 6.*以上
  2. jdbc.driverClassName = com.mysql.cj.jdbc.Driver
  3. jdbc.url = jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&pinGlobalTxToPhysicalConnection=true&useSSL=false
  4. jdbc.username =root
  5. jdbc.password =root
  6.  
  7. jdbc.two.url = jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&pinGlobalTxToPhysicalConnection=true&useSSL=false
  8. jdbc.two.username =root
  9. jdbc.two.password =root
  10.  

jta.properties

  1. com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
  2. com.atomikos.icatch.console_file_name=tm.release.out
  3. com.atomikos.icatch.log_base_name=tm.releaselog
  4. com.atomikos.icatch.tm_unique_name=com.atomikos.spring.jdbc.tm.release
  5. com.atomikos.icatch.console_log_level=INFO

TestInsert.java

  1. @ContextConfiguration(value = {"classpath:spring-application-context.xml"})
  2. @RunWith(SpringJUnit4ClassRunner.class)
  3. public class TestInsert {
  4. private Logger logger = LoggerFactory.getLogger(TestInsert.class);
  5. @Autowired
  6. private BatchInsertService batchInsertService;
  7.  
  8. @Test
  9. public void insert(){
  10. long startTime = System.currentTimeMillis();
  11. User user = new User();
  12. user.setName("User_"+(int)(Math.random()*100));
  13. user.setAge((int)(Math.random()*100));
  14. CustInfo info = new CustInfo();
  15. info.setPhone("123456789"+(int)(Math.random()*100));
  16. batchInsertService.insert(user,info);
  17. long endTime = System.currentTimeMillis();
  18. logger.info("共耗时:{}毫秒",endTime -startTime);
  19. }
  20. }
  21.  

BatchInsertService.java

  1. @Service
  2. public class BatchInsertService {
  3. private Logger logger = LoggerFactory.getLogger(BatchInsertService.class);
  4. @Autowired
  5. private UserService userService;
  6. @Autowired
  7. private CustInfoService custInfoService;
  8. @Transactional(rollbackFor= {Exception.class,RuntimeException.class})
  9. public void insert(User user,CustInfo custInfo) {
  10. int insertUser = userService.insert(user);
  11. logger.info("insertUser={}",insertUser);
  12. int insertCustInfo = custInfoService.insert(custInfo);
  13. logger.info("insertCustInfo={}",insertCustInfo);
  14. }
  15. }

UserService.java

  1. @Service
  2. public class UserService {
  3. @Autowired
  4. private UserMapper userMapper;
  5. public int insert(User record) {
  6. int result = userMapper.insert(record);
  7. return result;
  8. }
  9. }

CustInfoService.java

  1. @Service
  2. public class CustInfoService {
  3. @Autowired
  4. private CustInfoMapper custInfoMapper;
  5.  
  6. public int insert(CustInfo record) {
  7. int result = custInfoMapper.insert(record);
  8. long now = System.currentTimeMillis();
  9. // 模拟一个异常
  10. if (now % 2 == 0) {
  11. throw new RuntimeException("CustInfoMapper throws test insert exception");
  12. }
  13. return result;
  14. }
  15. }
  16.  

Mapper和Bean等就不列出来了,完成的示例工程在github: https://github.com/fenglibin/DruidWithAtomikos

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号