spring-boot入门(七)atomikos+druid+多数据源下的分布式事务配置

本章内容是基于spring-boot入门(六)多数据源
<https://blog.csdn.net/qq447995687/article/details/79796421>
的基础之上进行的,如果还不了解多数据源怎么配置,请参考上一章节的内容。在上一章节的末尾我们遗留了一个问题:多数据源下的分布式事务问题。在分布式事务下我们需要使用JTA(Java
Transaction API)事务来处理事务,保证事务的强一致性,即要成功都成功,一个失败全部回滚。Atomikos
是一个为Java平台提供增值服务的并且开源类事务管理器,本章我们将使用Atomikos 来解决分布式事务问题。

1. 配置atomikos

JTA事务的配置需要配置:XA数据源和JTA事务管理器

1.1 引入atomikos依赖

spring boot集成atomikos,只需添加一下依赖即可
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>
spring-boot-starter-jta-atomikos</artifactId> </dependency>
1.2 自定义atomikos相关属性(自动注入JTA事务管理器)

如果不自定义相关属性,那么spring boot
会初始化默认的atomikos配置,可看org.springframework.boot.autoconfigure.transaction.jta.AtomikosJtaConfiguration是如何初始化默认的配置。当然也可以自定义atomikos,通过该配置spring
boot会启用atomikos实现的JTA事务管理器。
spring: jta: atomikos: properties: max-actives: 50 max-timeout: 300000 default
-jta-timeout: 10000 enable-logging: true

上面是atomikos的一个配置示例。更多可查看org.springframework.boot.jta.atomikos.AtomikosProperties。

1.3 配置XA数据源
@Bean @Primary @ConfigurationProperties(prefix = "boc.datasource") public
DataSourcebocDataSource(@Qualifier("bocDataSourceProperties")
DataSourceProperties dataSourceProperties)throws SQLException {
DruidXADataSource druidXADataSource =new DruidXADataSource();
InitDruidDataSource(druidXADataSource, dataSourceProperties);
AtomikosDataSourceBean atomikosDataSource =new AtomikosDataSourceBean();
atomikosDataSource.setUniqueResourceName("bocDataSource");
atomikosDataSource.setXaDataSource(druidXADataSource);
atomikosDataSource.setTestQuery("SELECT 1"); return atomikosDataSource; } @Bean
@ConfigurationProperties(prefix = "ccb.datasource") public DataSource
ccbDataSource(@Qualifier("ccbDataSourceProperties") DataSourceProperties
dataSourceProperties)throws SQLException { DruidXADataSource druidXADataSource =
new DruidXADataSource(); InitDruidDataSource(druidXADataSource,
dataSourceProperties);//也可以使用mysql默认的xa数据源 //MysqlXADataSource
mysqlXADataSource = new MysqlXADataSource();
//mysqlXADataSource.setUrl(dataSourceProperties.getUrl());
//mysqlXADataSource.setUser(dataSourceProperties.getUsername());
//mysqlXADataSource.setPassword(dataSourceProperties.getPassword());
AtomikosDataSourceBean atomikosDataSource =new AtomikosDataSourceBean();
atomikosDataSource.setUniqueResourceName("ccbDataSource");
atomikosDataSource.setXaDataSource(druidXADataSource);
atomikosDataSource.setTestQuery("SELECT 1"); return atomikosDataSource; }
在这里我们返回的是AtomikosDataSourceBean
,是Druid实现的一个XA数据源也可以用注释中的MysqlXADataSource,配置了XAdatasource还需要将其转换为AtomikosDataSourceBean(Atomikos使用的是该数据源,用于包装不同的XA数据源实现,使得底层架构不变,对扩展开放),springboot默认配置的时候会使用AtomikosXADataSourceWrapper来包裹XAdatasource:

因为我们是手动注入DataSource,所以需要自己重新包装一次。

到此XA数据源和JTA事务都配置完毕,这时使用@Transactional注解将使用atomikos实现的事务管理器。

2.测试


还是沿用上节的测试代码,当我们执行testTransferAccountCcbToBocOnException()测试方法,会发现ccb库的数据成功回滚。而且根据console日志能查看事务提交和回滚的完整过程:
2018-04-02 23:15:49.536 INFO 14076 --- [ main]
c.a.icatch.imp.BaseTransactionManager : createCompositeTransaction (10000 ):
created new ROOT transaction with id10.100.144.36.tm0000100001 2018-04-02 23:15:
49.574 INFO 14076 --- [ main] c.atomikos.jdbc.AbstractDataSourceBean :
AtomikosDataSoureBean'ccbDataSource': getConnection ( null )... 2018-04-02 23:15
:49.574 INFO 14076 --- [ main] c.atomikos.jdbc.AbstractDataSourceBean :
AtomikosDataSoureBean'ccbDataSource': init... 2018-04-02 23:15:49.628 INFO 14076
--- [ main] c.a.icatch.imp.CompositeTransactionImp : addParticipant (
XAResourceTransaction:
31302E3130302E3134342E33362E746D30303030313030303031:31302E3130302E3134342E33362E746D31
)for transaction 10.100.144.36.tm0000100001 2018-04-02 23:15:49.628 INFO 14076
--- [ main] c.a.datasource.xa.XAResourceTransaction : XAResource.start (
31302E3130302E3134342E33362E746D30303030313030303031:31302E3130302E3134342E33362E746D31
, XAResource.TMNOFLAGS ) on resource ccbDataSource represented by XAResource
instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@3bc18fec2018-04-02 23:
15:49.689 INFO 14076 --- [ main] c.a.icatch.imp.CompositeTransactionImp :
registerSynchronization (
com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@a9775fbb )
for transaction 10.100.144.36.tm0000100001 2018-04-02 23:15:49.689 INFO 14076
--- [ main] c.atomikos.jdbc.AtomikosConnectionProxy : atomikos connection proxy
for com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@6c8d8b60: calling
prepareStatement(UPDATE account_ccb SET balance = balance+? WHERE customer = ?;)
... 2018-04-02 23:15:50.358 INFO 14076 --- [ main]
c.f.service.impl.AccountCcbServiceImpl : update balance ,[customer:10002
,amount:-100] 2018-04-02 23:15:50.364 INFO 14076 --- [ main]
c.atomikos.jdbc.AbstractDataSourceBean : AtomikosDataSoureBean'bocDataSource':
getConnection ( null )... 2018-04-02 23:15:50.364 INFO 14076 --- [ main]
c.atomikos.jdbc.AbstractDataSourceBean : AtomikosDataSoureBean'bocDataSource':
init...2018-04-02 23:15:50.367 INFO 14076 --- [ main]
c.a.icatch.imp.CompositeTransactionImp : addParticipant (
XAResourceTransaction:
31302E3130302E3134342E33362E746D30303030313030303031:31302E3130302E3134342E33362E746D32
)for transaction 10.100.144.36.tm0000100001 2018-04-02 23:15:50.374 INFO 14076
--- [ main] c.a.datasource.xa.XAResourceTransaction : XAResource.start (
31302E3130302E3134342E33362E746D30303030313030303031:31302E3130302E3134342E33362E746D32
, XAResource.TMNOFLAGS ) on resource bocDataSource represented by XAResource
instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@33a8f5532018-04-02 23:
15:50.379 INFO 14076 --- [ main] c.a.icatch.imp.CompositeTransactionImp :
registerSynchronization (
com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@a9775fbb )
for transaction 10.100.144.36.tm0000100001 2018-04-02 23:15:50.379 INFO 14076
--- [ main] c.atomikos.jdbc.AtomikosConnectionProxy : atomikos connection proxy
for com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5fe46d52: calling
prepareStatement(UPDATE account_boc SET balance = balance+? WHERE customer = ?;)
... 2018-04-02 23:15:50.451 INFO 14076 --- [ main]
c.a.icatch.imp.CompositeTransactionImp : setRollbackOnly() calledfor transaction
10.100.144.36.tm0000100001 2018-04-02 23:15:50.452 INFO 14076 --- [ main]
c.atomikos.jdbc.AtomikosConnectionProxy : atomikos connection proxyfor
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@6c8d8b60: close()... 2018-04-02
23:15:50.452 INFO 14076 --- [ main] c.a.datasource.xa.XAResourceTransaction :
XAResource.end (
31302E3130302E3134342E33362E746D30303030313030303031:31302E3130302E3134342E33362E746D31
, XAResource.TMSUCCESS ) on resource ccbDataSource represented by XAResource
instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@3bc18fec2018-04-02 23:
15:50.453 INFO 14076 --- [ main] c.atomikos.jdbc.AtomikosConnectionProxy :
atomikos connection proxyfor
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5fe46d52: close()... 2018-04-02
23:15:50.453 INFO 14076 --- [ main] c.a.datasource.xa.XAResourceTransaction :
XAResource.end (
31302E3130302E3134342E33362E746D30303030313030303031:31302E3130302E3134342E33362E746D32
, XAResource.TMSUCCESS ) on resource bocDataSource represented by XAResource
instance com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@33a8f5532018-04-02 23:
15:50.456 INFO 14076 --- [ main] c.a.datasource.xa.XAResourceTransaction :
XAResource.rollback (
31302E3130302E3134342E33362E746D30303030313030303031:31302E3130302E3134342E33362E746D31
) on resource ccbDataSource represented by XAResource instance
com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@3bc18fec2018-04-02 23:15:50.583
INFO14076 --- [ main] c.a.datasource.xa.XAResourceTransaction :
XAResource.rollback (
31302E3130302E3134342E33362E746D30303030313030303031:31302E3130302E3134342E33362E746D32
) on resource bocDataSource represented by XAResource instance
com.mysql.jdbc.jdbc2.optional.MysqlXAConnection@33a8f5532018-04-02 23:15:50.587
INFO14076 --- [ main] c.a.icatch.imp.CompositeTransactionImp : rollback() done
of transaction10.100.144.36.tm0000100001

在上面的日志中我们可以看到,当其中一个事务失败,会JTA调用setRollbackOnly(),然后XAResource连接调用各自的rollback()方法进行回滚。
到本章为止,使用spring
boot访问数据库,及多数据源和多数据源下的分布式事务都已经介绍完了,之后的章节可能会介绍如何使用拦截器和过滤器及其它的一些web应用中的常见技术在spring
boot中的应用。

本章完整代码在:
https://github.com/Json-Lin/spring-boot-practice/tree/master/spring-boot-practice-atomikos

<https://github.com/Json-Lin/spring-boot-practice/tree/master/spring-boot-practice-atomikos>

END