欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

分布式事务之xa:atomikos

程序员文章站 2022-03-03 12:28:48
...

须知:该博客是多数据源的事务问题,还未涉及微服务的事务

 

1、导入核心的依赖包

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>

2、配置多数据源

##account表数据库配置
spring.atomikos.datasource.account.max-pool-size=25
spring.atomikos.datasource.account.min-pool-size=3
spring.atomikos.datasource.account.max-lifetime=20000
spring.atomikos.datasource.account.borrow-connection-timeout=10000
spring.atomikos.datasource.account.unique-resource-name=account
spring.atomikos.datasource.account.xa-properties.url=jdbc:mysql://127.0.0.1:3306/xa_account?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
spring.atomikos.datasource.account.xa-properties.username=root
spring.atomikos.datasource.account.xa-properties.password=root
spring.atomikos.datasource.account.xa-properties.driverClassName=com.mysql.jdbc.Driver
# 初始化大小,最小,最大
spring.atomikos.datasource.account.xa-properties.initialSize=10
spring.atomikos.datasource.account.xa-properties.minIdle=20
spring.atomikos.datasource.account.xa-properties.maxActive=100
## 配置获取连接等待超时的时间
spring.atomikos.datasource.account.xa-properties.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.atomikos.datasource.account.xa-properties.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.atomikos.datasource.account.xa-properties.minEvictableIdleTimeMillis=300000
spring.atomikos.datasource.account.xa-properties.testWhileIdle=true
spring.atomikos.datasource.account.xa-properties.testOnBorrow=false
spring.atomikos.datasource.account.xa-properties.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.atomikos.datasource.account.xa-properties.poolPreparedStatements=true
spring.atomikos.datasource.account.xa-properties.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.atomikos.datasource.account.xa-properties.filters=stat,slf4j,wall
spring.atomikos.datasource.account.xa-data-source-class-name=com.alibaba.druid.pool.xa.DruidXADataSource

#------------------------------ 分隔符-------------------------------------
##redpacket表数据库配置
spring.atomikos.datasource.redpacket.max-pool-size=25
spring.atomikos.datasource.redpacket.min-pool-size=3
spring.atomikos.datasource.redpacket.max-lifetime=20000
spring.atomikos.datasource.redpacket.borrow-connection-timeout=10000
spring.atomikos.datasource.redpacket.unique-resource-name=redpacket
spring.atomikos.datasource.redpacket.xa-properties.url=jdbc:mysql://127.0.0.1:3306/xa_red_account?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
spring.atomikos.datasource.redpacket.xa-properties.username=root
spring.atomikos.datasource.redpacket.xa-properties.password=root
spring.atomikos.datasource.redpacket.xa-properties.driverClassName=com.mysql.jdbc.Driver
spring.atomikos.datasource.redpacket.xa-properties.initialSize=10
spring.atomikos.datasource.redpacket.xa-properties.minIdle=20
spring.atomikos.datasource.redpacket.xa-properties.maxActive=100
spring.atomikos.datasource.redpacket.xa-properties.maxWait=60000
spring.atomikos.datasource.redpacket.xa-properties.timeBetweenEvictionRunsMillis=60000
spring.atomikos.datasource.redpacket.xa-properties.minEvictableIdleTimeMillis=300000
spring.atomikos.datasource.redpacket.xa-properties.testWhileIdle=true
spring.atomikos.datasource.redpacket.xa-properties.testOnBorrow=false
spring.atomikos.datasource.redpacket.xa-properties.testOnReturn=false
spring.atomikos.datasource.redpacket.xa-properties.poolPreparedStatements=true
spring.atomikos.datasource.redpacket.xa-properties.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.atomikos.datasource.redpacket.xa-properties.filters=stat,slf4j,wall
spring.atomikos.datasource.redpacket.xa-data-source-class-name=com.alibaba.druid.pool.xa.DruidXADataSource




logging.level.root=INFO

3、第三步:将配置的数据库连接信息,注入数据源,并且设置druid的监控中心。

MybatisConfiguration :配置DataSource

@Configuration
@EnableConfigurationProperties
@EnableTransactionManagement(proxyTargetClass = true)
public class MybatisConfiguration {
    /**
     * account数据库配置前缀.
     */
    final static String ACCOUNT_PREFIX = "spring.atomikos.datasource.account";
    /**
     * redpacket数据库配置前缀.
     */
    final static String REDPACKET_PREFIX = "spring.atomikos.datasource.redpacket";

    /**
     * The constant logger.
     */
    final static Logger logger = LoggerFactory.getLogger(MybatisConfiguration.class);

    /**
     * 配置druid显示监控统计信息
     * 开启Druid的监控平台 http://localhost:8080/druid
     *
     * @return servlet registration bean
     */
    @Bean
    public ServletRegistrationBean druidServlet() {
        logger.info("Init Druid Servlet Configuration ");
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        // IP白名单,不设默认都可以
//        servletRegistrationBean.addInitParameter("allow", "192.168.2.25,127.0.0.1");
        // IP黑名单(共同存在时,deny优先于allow)
        servletRegistrationBean.addInitParameter("deny", "192.168.1.100");
        //控制台管理用户
        servletRegistrationBean.addInitParameter("loginUsername", "root");
        servletRegistrationBean.addInitParameter("loginPassword", "root");
        //是否能够重置数据 禁用HTML页面上的“Reset All”功能
        servletRegistrationBean.addInitParameter("resetEnable", "false");
        return servletRegistrationBean;
    }

    /**
     * 注册一个filterRegistrationBean
     *
     * @return filter registration bean
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
        //添加过滤规则
        filterRegistrationBean.addUrlPatterns("/*");
        //添加不需要忽略的格式信息
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }

    /**
     * 配置Account数据库的数据源
     *
     * @return the data source
     */
    @Bean(name = "AccountDataSource")
    @ConfigurationProperties(prefix = ACCOUNT_PREFIX)  // application.properties中对应属性的前缀
    public DataSource accountDataSource() {
        return new AtomikosDataSourceBean();
    }

    /**
     * 配置RedPacket数据库的数据源
     *
     * @return the data source
     */
    @Bean(name = "RedPacketDataSource")
    @ConfigurationProperties(prefix = REDPACKET_PREFIX)  // application.properties中对应属性的前缀
    public DataSource redPacketDataSource() {
        return new AtomikosDataSourceBean();
    }
}

AccountDataSourceConfiguration 作用:配置account的数据源的sessionfactory ,同时关联mybaits

@Configuration
@MapperScan(basePackages = {"com.agan.dtp.atomikos.mapper.account.mapper"}, sqlSessionFactoryRef = "accountSqlSessionFactory")
public class AccountDataSourceConfiguration {
    /**
     * The constant MAPPER_XML_LOCATION.
     */
    public static final String MAPPER_XML_LOCATION = "classpath*:com/transaction/dtp/atomikos/mapper/account/mapper/*.xml";

    /**
     * The Open plat form data source.
     */
    @Autowired
    @Qualifier("AccountDataSource")
    DataSource accountDataSource;

    /**
     * 配置Sql Session模板
     *
     * @return the sql session template
     * @throws Exception the exception
     */
    @Bean
    public SqlSessionTemplate springSqlSessionTemplate() throws Exception {
        return new SqlSessionTemplate(accountSqlSessionFactory());
    }

    /**
     * 配置SQL Session工厂
     *
     * @return the sql session factory
     * @throws Exception the exception
     */
    @Bean
    public SqlSessionFactory accountSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(accountDataSource);
        //指定XML文件路径
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_XML_LOCATION));
        return factoryBean.getObject();
    }
}

RedAccountDataSourceConfiguration 作用:配置RedAccount的数据源的sessionfactory ,同时关联mybaits

@Configuration
@MapperScan(basePackages = {"com.agan.dtp.atomikos.mapper.redaccount.mapper"}, sqlSessionFactoryRef = "redPacketSqlSessionFactory")
public class RedAccountDataSourceConfiguration {
    /**
     * The constant MAPPER_XML_LOCATION.
     */
    public static final String MAPPER_XML_LOCATION = "classpath*:com/transaction/dtp/atomikos/mapper/redaccount/mapper/*.xml";

    /**
     * The Open plat form data source.
     */
    @Autowired
    @Qualifier("RedPacketDataSource")
    DataSource redPacketDataSource;

    /**
     * Open plat form sql session template sql session template.
     *
     * @return the sql session template
     * @throws Exception the exception
     */
    @Bean
    public SqlSessionTemplate redPacketSqlSessionTemplate() throws Exception {
        return new SqlSessionTemplate(redPacketSqlSessionFactory());
    }

    /**
     * Open plat form sql session factory sql session factory.
     *
     * @return the sql session factory
     * @throws Exception the exception
     */
    @Bean
    public SqlSessionFactory redPacketSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(redPacketDataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_XML_LOCATION));
        return factoryBean.getObject();
    }
}

第四步:service体验 atomikos

@Service
public class PayServiceImpl implements PayService {

    @Autowired
    private CapitalAccountMapper capitalAccountMapper;
    @Autowired
    private RedPacketAccountMapper redPacketAccountMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pay(int userId,int account, int redAccount) {
        CapitalAccount ca=new CapitalAccount();
        ca.setUserId(userId);
        CapitalAccount capitalDTO=this.capitalAccountMapper.selectOne(ca);
        System.out.println(capitalDTO);
        //账户余额扣除
        capitalDTO.setBalanceAmount(capitalDTO.getBalanceAmount()-account);
        this.capitalAccountMapper.updateByPrimaryKey(capitalDTO);

        RedPacketAccount red= new RedPacketAccount();
        red.setUserId(userId);
        RedPacketAccount redDTO=this.redPacketAccountMapper.selectOne(red);
        System.out.println(redDTO);
        //红包余额扣除
        redDTO.setBalanceAmount(redDTO.getBalanceAmount()-redAccount);
        this.redPacketAccountMapper.updateByPrimaryKey(redDTO);
        //int n=9/0;//测试
    }
}

第五步: junit 测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class AtomikosTests {

    @Autowired
    PayService payService;

    @Test
    //@Transactional
    public void contextLoads() {
        try {
           this.payService.pay(1,10,10);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

第六步: 查看Atomikos的日志
1.如何测试atomikos的事务运行结果?
  查看Atomikos的日志,默认情况下,在项目的根目录下会自动创建transaction-logs文件夹,每个Atomikos实例都会有一个全局ID,这个ID为Atomikos运行机器的IP地址;
  这个唯一ID会自动关联多个数据库的事务信息,也就是会关联分支事务id.
 
2.atomikos日志的自定义配置
spring.jta.log-dir=transaction-logs

相关标签: 分布式事务