1. 程式人生 > >spring-boot入門(七)atomikos+druid+多資料來源下的分散式事務配置

spring-boot入門(七)atomikos+druid+多資料來源下的分散式事務配置

spring-boot入門(七)atomikos+druid+多資料來源下的分散式事務配置

本章內容是基於spring-boot入門(六)多資料來源的基礎之上進行的,如果還不瞭解多資料來源怎麼配置,請參考上一章節的內容。在上一章節的末尾我們遺留了一個問題:多資料來源下的分散式事務問題。在分散式事務下我們需要使用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 DataSource bocDataSource(@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 id 10.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 [email protected]
2018-04-02 23:15:49.689  INFO 14076 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.a[email protected]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 [email protected]: 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 [email protected]
2018-04-02 23:15:50.379  INFO 14076 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : registerSynchronization ( com.a[email protected]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 [email protected]: 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() called for transaction 10.100.144.36.tm0000100001
2018-04-02 23:15:50.452  INFO 14076 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for [email protected]: 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 [email protected]
2018-04-02 23:15:50.453  INFO 14076 --- [           main] c.atomikos.jdbc.AtomikosConnectionProxy  : atomikos connection proxy for [email protected]: 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 [email protected]
2018-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 [email protected]
2018-04-02 23:15:50.583  INFO 14076 --- [           main] c.a.datasource.xa.XAResourceTransaction  : XAResource.rollback ( 31302E3130302E3134342E33362E746D30303030313030303031:31302E3130302E3134342E33362E746D32 ) on resource bocDataSource represented by XAResource instance [email protected]
2018-04-02 23:15:50.587  INFO 14076 --- [           main] c.a.icatch.imp.CompositeTransactionImp   : rollback() done of transaction 10.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

END