分布式事务Atomikos介绍与实践
分布式事务admin 发布于:2023-02-03 16:44:01
阅读:loading
Spring Boot整合了Atomikos和Bitronix两个JTA(Java Transaction API)的实现,但在2.3.0的版本中Bitronix不推荐使用Bitronix了,所以重点来关注一下Atomikos的组件。Atomikos提供了两款分布式事务产品级解决方案,分别是ExtremeTransactions和TransactionsEssentials,前者需要商业授权,后者是开源免费使用版本,TransactionsEssentials是免费的,可无缝升级为ExtremeTransactions版本,选择官方说是否商用版本取决于您是否认真对待事务,并希望组成自己的轻量级运行时以取代传统的应用程序服务器,反正对于个人来讲闭着眼睛肯定是开源免费版本。
Atomikos TransactionsEssentials是领先的开源JTA/XA分布式事务管理实现,使用JTA/XA和连接池,可用于应用服务器之外的自包含应用程序,它是一个无需应用服务器的事务管理器,比同类产品更容易使用和更成熟,关于为什么要使用Atomikos以及它的特性,可查看官网的更加详细说明:https://www.atomikos.com/Documentation/WhyUseAtomikos。假设在两台不同的机器上有两个进程(A和B),如何确保跨越这两个流程的任务的可靠性?换句话说:你怎么能确保A没有做部分功能(而B没有)或者相反,根据需要的内容和应用程序的性质,有不同的方法来实现这一点。关于JTA Atomikos的更多理论知识自行去百科,本次基于jta-atomikos + Spring Boot + MyBatils Plus/Spring Data JDBC + MySQL 来实现两个数据库的事务管理,先来看下项目结构如下图所示:
本次示例构建在一个多模块的项目下,两个业务功能模块分别对应了有两个不同的数据源,演示在不同数据源下利用用数据库持久层框架实现的事务一致性,同时提交或回滚。
(1)jta-atomikos是整个项目名称,起到定义使用框架版本信息和具体子模块的功能;
(2)jta-base是所有业务功能的子模块,定义各个模块的公用组件依赖和公共处理类;
(3)jta-bootstrap是项目的主启动模块,定义Spring Boot程序的启动;
(4)jta-users是业务功能模块,定义用户相关的模块功能;
(5)jta-bless是业务功能模块,随便定义另外的一块业务功能(由于实践阶段是过年正当时,起名bless寓意祝福和好运吧);
(1)jta-base独立,不依赖项目其它模块,参考maven pom.xml依赖(此处仅包含jta-base模块的公共依赖,springboot依赖为2.2.7.RELEASE版本)如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
<!-- 数据库相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
</dependencies>
(2)jta-users和jta-bless均依赖jta-base;
(3)jta-bootstrap依赖jta-users和jta-bless两个模块;
(1)包含多个数据源的定义(示例中数据源的参数只提供了最基本的4个常规必备参数,实际应用应该有更多的配置);
(2)使用XML配置管理Spring Bean组件的声明(特地再用XML配置的形式练习练习,毕竟很多场景下的XML配置优于Java代码);
(3)两个模块分别对应了两个不同机器的MySQL数据库,分别是本机127.0.0.1和本站的远程阿里云服务器上的MySQL,两个MySQL版本分别为5.7.26、5.7.33;
(4)示例中包含4个验证,分别是两个数据源使用MyBatis Plus Api的事务提交、两个数据库的使用MyBatis Plus Api的事务回滚;两个数据库使用MyBatis Plus Api和Spring Data Jdbc Api的事务回滚;
package cn.chendd.transactional.service.impl;
import cn.chendd.bless.mapper.GoodLuckMapper;
import cn.chendd.bless.model.GoodLuck;
import cn.chendd.transactional.service.TransactionalService;
import cn.chendd.users.mapper.UsersMapper;
import cn.chendd.users.model.Users;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
* 验证事务接口实现
*
* @author chendd
* @date 2023/1/20 16:29
*/
@Service
public class TransactionalServiceImpl implements TransactionalService {
/**
* 本机数据源
*/
@Resource
private UsersMapper usersMapper;
/**
* 远程数据源
*/
@Resource
private GoodLuckMapper goodLuckMapper;
@Resource(name = "blessNamedParameterJdbcTemplate")
private NamedParameterJdbcTemplate blessJdbcTemplate;
@Override
@Transactional(rollbackFor = Exception.class)
public void saveData() {
//构造用户数据对象并保存
Users user = new Users(null , "chendd" , "男" , "88911006@qq.com" , LocalDateTime.now().toString());
this.usersMapper.insert(user);
//构造好运数据对象并保存
GoodLuck goodLuck = new GoodLuck(null , "chendd" , "春节快乐!");
this.goodLuckMapper.insert(goodLuck);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveDataMyBatisPlus() {
this.saveData();
System.out.println("抛出异常,事务回滚...");
throwException();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveDataMyBatisPlusOrJdbcTemplate() {
//构造用户数据对象并保存
Users user = new Users(null , "chendd" , "男" , "88911006@qq.com" , LocalDateTime.now().toString());
this.usersMapper.insert(user);
//构造好运数据对象并保存
GoodLuck goodLuck = new GoodLuck(IdWorker.getId(), "chendd" , "春节快乐!");
String sql = "insert into test(id , name , remark) values (:id , :name , :remark)";
this.blessJdbcTemplate.batchUpdate(sql , SqlParameterSourceUtils.createBatch(goodLuck));
System.out.println("抛出异常,事务回滚...");
throwException();
}
@Override
public Users queryUsers() {
return this.usersMapper.queryUsers();
}
/**
* 抛出运行时异常
*/
private void throwException() {
System.out.println(1 / 0);
}
}
以上是Service接口的实现定义,UserMapper是本机127.0.0.1的MySQL数据源,GoodLuckMapper和blessJdbcTemplate是本站阿里云数据库服务器的数据源;
考虑文章的图片演示大小,录制的运行效果图比较简陋,大概只能看到发送了三次请求,其中每个请求都向不在同一台机器上的两个数据库表中插入数据,成功一次,失败两次,即数据库表中成功提交一次,各一条数据。特专门录制了一个更为详细的录屏示例过程,从服务器启动开始,一步一步的演示,一步一步的说明,值得观看,参考下方的源码下载。
(1)工程实例项目源码见:相关下载.zip;
(2)Atomikos事务集成的在服务器启动时的提示警告:“Thanks for using Atomikos!”,类似重要的事情说三次,参考如下图所示:
经过对源代码的分析和验证在resources目录下增加transactions.properties文件,并设置com.atomikos.icatch.registered = true参数,可起到注册atomikos的目的来屏蔽这段日志输出,参考如下图所示:
点赞