Springboot中使用jta+atomikos解决分布式事务

如何使用多数据源https://blog.csdn.net/kxj19980524/article/details/86751785

首先分布式事务是基于多数据源的情况下解决在一个方法中调用不同的数据源,如何来进行事务管理的.

在上面那节讲到了多数据源怎么使用事务,可以加注解指定哪个事务就可以了,但是分布式事务的话不可以.因为你一个方法只能有一个事务,方法里有不同的数据源,肯定不能指定某一个事务,这时该怎么办呢?

jta+atomikos就是用来解决这个问题的,它的原理就是把多个事务都交给它来统一管理,这样就不用单独指定使用哪个事务了,但是注意这种解决方式只局限于小型的项目,在大型互联网项目中是不可以使用这种方式的,因为它性能不好.

 

分布式事务其实有单独的一门技术,它是跨数据库的,就是在一个项目中有不同的数据库,mysq,oracle,等,要同时往不同的数据库中插入数据形成事务,但是它可能等的时间比较长,如果是给企业用的项目的话,等就等无所谓,但是如果是互联网项目的话,这么做肯定不可以,给用户体验太差,

解决办法是使用mq来解决,比如当用户购买商品提交订单的时候,后台可能要做好多时期,比如入账,出货等等等,如果给它们一个事务同时完成再给用户响应的话时间太长,不现实,我们需要做的是最终的一致性就可以了,而不是时时的一致,可以购买的时候使用mq给其它服务发送消息,然后立马给用户回馈,剩下的事可以慢慢做,但是如果抛异常中断了呢,数据就不一致了,其实mq有一种应答模式,叫手动应答,手动应答就是把消息正常处理完了才给服务器回馈,我已经成功了,才会把消息删掉,如果没正常处理完的话,可以重发,一直重发,直到正常响应成功才会删除消息,这是在电商项目中的解决办法.

 

使用步骤,这个步骤是在上一节的基础案例上改的

导入依赖



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

把数据源也修改一下,之前的用不到了 

mysql.datasource.test1.url = jdbc:mysql://localhost:3306/test01?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = 123

mysql.datasource.test1.minPoolSize = 3
mysql.datasource.test1.maxPoolSize = 25
mysql.datasource.test1.maxLifetime = 20000
mysql.datasource.test1.borrowConnectionTimeout = 30
mysql.datasource.test1.loginTimeout = 30
mysql.datasource.test1.maintenanceInterval = 60
mysql.datasource.test1.maxIdleTime = 60
mysql.datasource.test1.testQuery = select 1


# Mysql 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =123

mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60
mysql.datasource.test2.testQuery = select 1

写两个配置类来读取配置文件的数据源,我这就贴一个另一个和这个一样,就是前缀变一下

@ConfigurationProperties(prefix = "mysql.datasource.test2")这个注解就是从配置文件读取数据到这个实体类通过前缀的方式读取

Springboot中使用jta+atomikos解决分布式事务_第1张图片

package com.buba.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "mysql.datasource.test1")
@Data
public class DBConfig1 {

	private String url;
	private String username;
	private String password;
	private int minPoolSize;
	private int maxPoolSize;
	private int maxLifetime;
	private int borrowConnectionTimeout;
	private int loginTimeout;
	private int maintenanceInterval;
	private int maxIdleTime;
	private String testQuery;
}

然后在启动类上加上@EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class })让优先加载这两个配置类

Springboot中使用jta+atomikos解决分布式事务_第2张图片

然后编写两个把数据源放到atomikos统一管理的配置类,原来的配置类就不需要了

Springboot中使用jta+atomikos解决分布式事务_第3张图片

package com.buba.datasource;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.buba.config.DBConfig1;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import java.sql.SQLException;

@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "com.buba.test01", sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class TestMyBatisConfig1 {

	// 配置数据源
	@Bean
    @Primary
	public DataSource test1DataSource (DBConfig1 testConfig) throws SQLException {
		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
		mysqlXaDataSource.setUrl(testConfig.getUrl());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
		mysqlXaDataSource.setPassword(testConfig.getPassword());
		mysqlXaDataSource.setUser(testConfig.getUsername());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
		xaDataSource.setXaDataSource(mysqlXaDataSource);
		xaDataSource.setUniqueResourceName("test1DataSource");

		xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
		xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
		xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
		xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
		xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
		xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
		xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
		xaDataSource.setTestQuery(testConfig.getTestQuery());
		return xaDataSource;
	}

	@Bean(name = "test1SqlSessionFactory")
	public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	@Bean(name = "test1SqlSessionTemplate")
	public SqlSessionTemplate test1SqlSessionTemplate(
			@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

进行测试,在分布式事务中只要在方法上加@Transactional,就会自动进行统一管理了.访问这个方法就会发现两条数据都回滚了,都没有插入到数据库当中.

Springboot中使用jta+atomikos解决分布式事务_第4张图片

Springboot中使用jta+atomikos解决分布式事务_第5张图片

Springboot中使用jta+atomikos解决分布式事务_第6张图片

如果不使用分布式事务管理的话,在上一节中指定哪个事务的话会出现,test01插入进去了,test02回滚了,因为这个事务指定的是管理test02的而不是test01的

Springboot中使用jta+atomikos解决分布式事务_第7张图片

你可能感兴趣的:(springboot)