热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringBoot整合atomikos实现跨库事务的详细方案_java

这篇文章主要介绍了SpringBoot整合atomikos实现跨库事务,业务主要涉及政府及企业且并发量不大,所以采用XA事务,虽然性能有所损失,但是可以保证数据的强一致性,

背景

框架之前完成了多数据源的动态切换及事务的处理,想更近一步提供一个简单的跨库事务处理功能,经过网上的搜索调研,大致有XA事务/SEGA事务/TCC事务等方案,因为业务主要涉及政府及企业且并发量不大,所以采用XA事务,虽然性能有所损失,但是可以保证数据的强一致性

方案设计

针对注册的数据源拷贝一份用于XA事务,使得本地事务和XA全局事务相互独立可选择的使用

Maven配置

引入atomikos第三方组件


org.springframework.boot
spring-boot-starter-jta-atomikos

注册XA数据源

使用Druid连接池,需要使用DruidXADataSource数据源对象,再使用AtomikosDataSourceBean进行包装

注册数据源时针对同一个连接注册两份,一份正常数据源,一份用于XA事务的数据源,数据源标识区分并关联

因为spring默认注册了XA事务管理器后,所有事务操作不再走本地事务,我们通过切换不同的数据源决定走本地事务还是XA事务

//主数据源xa模式
@Bean
@Qualifier("masterXADataSource")
public DataSource masterXADataSource() {
DruidXADataSource datasource = new DruidXADataSource();
if(driverClassName.equals("com.mysql.cj.jdbc.Driver")){
if(!dbUrl.contains("useOldAliasMetadataBehavior")){
dbUrl += "&useOldAliasMetadataBehavior=true";
}
if(!dbUrl.contains("useAffectedRows")){
dbUrl += "&useAffectedRows=true";
}
}
datasource.setUrl(this.dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
//configuration
datasource.setInitialSize(1);
datasource.setMinIdle(3);
datasource.setMaxActive(20);
datasource.setMaxWait(60000);
datasource.setTimeBetweenEvictionRunsMillis(60000);
datasource.setMinEvictableIdleTimeMillis(60000);
datasource.setValidationQuery("select 'x'");
datasource.setTestWhileIdle(true);
datasource.setTestOnBorrow(false);
datasource.setTestOnReturn(false);
datasource.setPoolPreparedStatements(true);
datasource.setMaxPoolPreparedStatementPerConnectionSize(20);
datasource.setLogAbandoned(false); //移除泄露连接发生是是否记录日志
try {
datasource.setFilters("stat,slf4j");
} catch (SQLException e) {
logger.error("druid configuration initialization filter", e);
}
datasource.setConnectionProperties("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");//connectionProperties);
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
atomikosDataSourceBean.setUniqueResourceName("master-xa");
atomikosDataSourceBean.setXaDataSource(datasource);
atomikosDataSourceBean.setPoolSize(5);
atomikosDataSourceBean.setMaxPoolSize(20);
atomikosDataSourceBean.setTestQuery("select 1");
return atomikosDataSourceBean;
}

注册XA事务管理器

使用spring内置的JtaTransactionManager事务管理器对象,设置AllowCustomIsolationLevels为true,否则指定自定义事务隔离级别会报错

//xa模式全局事务管理器
@Bean(name = "jtaTransactionManager")
public PlatformTransactionManager transactionManager() throws Throwable {
UserTransactionManager userTransactiOnManager= new UserTransactionManager();
UserTransaction userTransaction = new UserTransactionImp();
JtaTransactionManager jtaTransactiOnManager= new JtaTransactionManager(userTransaction, userTransactionManager);
jtaTransactionManager.setAllowCustomIsolationLevels(true);
return jtaTransactionManager;
}

定义XA事务切面

自定义注解@GlobalTransactional并定义对应切面,使用指定注解时在ThreadLocal变量值进行标识,组合

@Transactional注解指定XA事务管理器,在切换数据原时判断当前是否在XA事物中,从而切换不同的数据源

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_UNCOMMITTED,transactiOnManager= "jtaTransactionManager")
public @interface GlobalTransactional {
}


@Aspect
@Component
@Order(value = 99)
public class GlobalTransitionAspect {
private static Logger logger = LoggerFactory.getLogger(GlobalTransitionAspect.class);
@Autowired
private DynamicDataSource dynamicDataSource;
/**
* 切面点 指定注解
*/
@Pointcut("@annotation(com.code2roc.fastkernel.datasource.GlobalTransactional) " +
"|| @within(com.code2roc.fastkernel.datasource.GlobalTransactional)")
public void dataSourcePointCut() {
}
/**
* 拦截方法指定为 dataSourcePointCut
*/
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
GlobalTransactional methodAnnotation = method.getAnnotation(GlobalTransactional.class);
if (methodAnnotation != null) {
DataSourceContextHolder.tagGlobal();
logger.info("标记全局事务");
}
try {
return point.proceed();
} finally {
logger.info("清除全局事务");
DataSourceContextHolder.clearGlobal();
}
}
}


public class DataSourceContextHolder {
private static Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);
// 对当前线程的操作-线程安全的
private static final ThreadLocal cOntextHolder= new ThreadLocal();
private static final ThreadLocal cOntextGlobalHolder= new ThreadLocal();
// 调用此方法,切换数据源
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
log.debug("已切换到数据源:{}", dataSource);
}
// 获取数据源
public static String getDataSource() {
String value = contextHolder.get();
if (StringUtil.isEmpty(value)) {
value = "master";
}
if (!StringUtil.isEmpty(getGlobal())) {
value = value + "-xa";
}
return value;
}
// 删除数据源
public static void clearDataSource() {
contextHolder.remove();
log.debug("已切换到主数据源");
}
//====================全局事务标记================
public static void tagGlobal() {
contextGlobalHolder.set("1");
}
public static String getGlobal() {
String value = contextGlobalHolder.get();
return value;
}
public static void clearGlobal() {
contextGlobalHolder.remove();
}
//===================================================
}

配置XA事务日志

通过在resource文件夹下创建transactions.properties文件可以指定XA事务日志的存储路径

com.atomikos.icatch.log_base_dir= tempfiles/transition/


推荐阅读
  • PHP函数实现分页含文本分页和数字分页【PHP】
    后端开发|php教程PHP,分页后端开发-php教程最近,在项目中要用到分页。分页功能是经常使用的一个功能,所以,对其以函数形式进行了封装。影视网源码带充值系统,vscode配置根 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • 数据库锁的分类和应用
    本文介绍了数据库锁的分类和应用,包括并发控制中的读-读、写-写、读-写/写-读操作的问题,以及不同的锁类型和粒度分类。同时还介绍了死锁的产生和避免方法,并详细解释了MVCC的原理以及如何解决幻读的问题。最后,给出了一些使用数据库锁的实际场景和建议。 ... [详细]
  • 如何在php中将mysql查询结果赋值给变量
    本文介绍了在php中将mysql查询结果赋值给变量的方法,包括从mysql表中查询count(学号)并赋值给一个变量,以及如何将sql中查询单条结果赋值给php页面的一个变量。同时还讨论了php调用mysql查询结果到变量的方法,并提供了示例代码。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 本文提供了关于数据库设计的建议和注意事项,包括字段类型选择、命名规则、日期的加入、索引的使用、主键的选择、NULL处理、网络带宽消耗的减少、事务粒度的控制等方面的建议。同时还介绍了使用Window Functions进行数据处理的方法。通过遵循这些建议,可以提高数据库的性能和可维护性。 ... [详细]
  • 本文介绍了Hive常用命令及其用途,包括列出数据表、显示表字段信息、进入数据库、执行select操作、导出数据到csv文件等。同时还涉及了在AndroidManifest.xml中获取meta-data的value值的方法。 ... [详细]
  • JAVA调用存储过程CallableStatement对象的方法及使用示例
    本文介绍了使用JAVA调用存储过程CallableStatement对象的方法,包括创建CallableStatement对象、传入IN参数、注册OUT参数、传入INOUT参数、检索结果和OUT参数、处理NULL值等。通过示例代码演示了具体的调用过程。 ... [详细]
author-avatar
留难龚_431
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有