SpringBoot多資料來源中的分散式事務
雖然現在微服務越來越流行,我們的系統隨之也拆分出來好多的模組功能。這樣做的目的其實就是為了彌補單體架構中存在的不足。隨著微服務的拆分,肯定設計到分庫分表,但這之中肯定設計到分散式事務。最典型的例子就是銀行轉賬,比如銀行A給銀行B轉賬500 塊錢,流程肯定是銀行A-500,銀行B+500,在這個過程要麼都成功,要麼都成仁。首先銀行A和銀行B的數肯定是在不同的資料庫,如果在轉賬的過程中,銀行A首先-500庫錢之後,在銀行B+500的時候出現了問題,如果事務不回滾,那麼就會出現500塊錢丟失的問題,也就是出現了事務一致性問題。
JTA + Atomikos解決分散式事務
一、JTA
JTA(java Transaction API)是JavaEE 13 個開發規範之一。Java 事務API,允許應用程式執行分散式事務處理——在兩個或多個網路計算機資源上訪問並且更新資料。JDBC驅動程式的JTA支援極大地增強了資料訪問能力。事務最簡單最直接的目的就是保證資料的有效性,資料的一致性。
二、Atomikos
Atomikos是一個為Java平臺提供增值服務的並且開源類事務管理器。
工作原理:分散式事務包括事務管理器和支援XA的資源管理器。資源管理器就是我們的DB,事務管理器就是承擔調節和控制所有參與DB所設計到的事務。
個人理解:Atomikos 獲取到資料庫的連線之後,會遮蔽資料庫底層的事務控制,然後全部交給 Atomikos,進行統一的排程和控制。
接下來,我們簡單的做一個基於 SpringBoot 的分散式事務控制。
1、首先我們要引入需要引入的maven庫
<!--分散式事務-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
2、配置資料來源
3、配置類
最主要的配置
@Configuration
@MapperScan(basePackages = "com.example.mapper.db1", sqlSessionFactoryRef = "db1SqlSessionFactory")
public class DB1DataSourcesConfig {
@Primary
@Bean(name = "db1DataSource")
public DataSource dataSource(DB1Config DB1Config) {
// 設定資料庫連線
MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
mysqlXADataSource.setUrl(DB1Config.getUrl_jdbc());
mysqlXADataSource.setUser(DB1Config.getUsername());
mysqlXADataSource.setPassword(DB1Config.getPassword());
mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
// 交給事務管理器進行管理
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(mysqlXADataSource);
atomikosDataSourceBean.setUniqueResourceName("db1DataSource");
return atomikosDataSourceBean;
}
@Primary
@Bean(name = "db1SqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/db1/*.xml"));
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
sessionFactoryBean.setConfiguration(configuration);
return sessionFactoryBean.getObject();
}
@Primary
@Bean(name = "db1SqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
這樣基本就配置完了,這裡我只寫了一個數據庫的配置,另外一個同這個一樣,就是資料庫資訊變了而已。
4、測試
這裡設計到兩個資料的修改,學生姓名和學生分數成績,這兩個資料分別儲存在不同的資料庫。我們通過介面先修改使用者資訊,接著再修改使用者的分數資訊。
沒有修改前的資料
我們先沒不加事務,先看下如果再修改分數的時候出現異常,使用者資訊會不會回滾回去。
結果
首先程式碼在39行報錯,如果按照我們的邏輯,如果修改分數失敗,那麼相應的使用者的年齡修改也是不成功的,但是結果顯示使用者的年齡還是被修改了,顯然這個介面並沒有被分散式事務所管理。
總結:以上就是 JTA + Atomikos 實現分散式事務的整個過程,相對實現功能來說還是比較簡單的,以上的測試是基於單個的 SpringBoot 專案的,相對於真正的微服務來說,這樣的方式我還沒有進行測試,如果需要進行微服務之間的事務管理,也可以通過 mq 或者 dubbo 去進行事務的一致性管理。