Shardingsphere整合Atomikos对XA分布式事务的支持(2)
Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款相互独立,却又能够混合部署配合使用的产品组成。它们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。
ShardingSphere 已于2020年4月16日成为 Apache 软件基金会的顶级项目。
咱们话不多,接上篇,我们直接进入正题。
Atomikos简单介绍
Atomikos(https://www.atomikos.com/),其实是一家公司的名字,提供了基于JTA规范的XA分布式事务TM的实现
。其旗下最著名的产品就是事务管理器。产品分两个版本:
-
TransactionEssentials:开源的免费产品;
-
ExtremeTransactions:上商业版,需要收费。
这两个产品的关系如下图所示:
ExtremeTransactions在TransactionEssentials的基础上额外提供了以下功能(重要的):
-
支持TCC:这是一种柔性事务
-
支持通过RMI、IIOP、SOAP这些远程过程调用技术,进行事务传播。
-
事务日志云存储,云端对事务进行恢复,并且提供了完善的管理后台。
org.apache.shardingsphere.transaction.xa.XAShardingTransactionManager详解
我们简单的来回顾下org.apache.shardingsphere.transaction.spiShardingTransactionManager
public interface ShardingTransactionManager extends AutoCloseable {
/**
* Initialize sharding transaction manager.
*
* @param databaseType database type
* @param resourceDataSources resource data sources
*/
void init(DatabaseType databaseType, Collection<ResourceDataSource> resourceDataSources);
/**
* Get transaction type.
*
* @return transaction type
*/
TransactionType getTransactionType();
/**
* Judge is in transaction or not.
*
* @return in transaction or not
*/
boolean isInTransaction();
/**
* Get transactional connection.
*
* @param dataSourceName data source name
* @return connection
* @throws SQLException SQL exception
*/
Connection getConnection(String dataSourceName) throws SQLException;
/**
* Begin transaction.
*/
void begin();
/**
* Commit transaction.
*/
void commit();
/**
* Rollback transaction.
*/
void rollback();
}
我们重点县关注init
方法,从它的命名,你就应该能够看出来,这是整个框架的初始化方法,让我们来看看它是如何进行初始化的。
private final Map<String, XATransactionDataSource> cachedDataSources = new HashMap<>();
private final XATransactionManager xaTransactionManager = XATransactionManagerLoader.getInstance().getTransactionManager();
@Override
public void init(final DatabaseType databaseType, final Collection<ResourceDataSource> resourceDataSources) {
for (ResourceDataSource each : resourceDataSources) {
cachedDataSources.put(each.getOriginalName(), new XATransactionDataSource(databaseType, each.getUniqueResourceName(), each.getDataSource(), xaTransactionManager));
}
xaTransactionManager.init();
}
-
首先SPI的方式加载XATransactionManager的具体实现类,这里返回的就是
org.apache.shardingsphere.transaction.xa.atomikos.manager.AtomikosTransactionManager
。 -
我们在关注下
new XATransactionDataSource()
, 进入org.apache.shardingsphere.transaction.xa.jta.datasource。XATransactionDataSource
类的构造方法。
public XATransactionDataSource(final DatabaseType databaseType, final String resourceName, final DataSource dataSource, final XATransactionManager xaTransactionManager) {
this.databaseType = databaseType;
this.resourceName = resourceName;
this.dataSource = dataSource;
if (!CONTAINER_DATASOURCE_NAMES.contains(dataSource.getClass().getSimpleName())) {
// 重点关注 1 ,返回了xaDatasource
xaDataSource = XADataSourceFactory.build(databaseType, dataSource);
this.xaTransactionManager = xaTransactionManager;
// 重点关注2 注册资源
xaTransactionManager.registerRecoveryResource(resourceName, xaDataSource);
}
}
-
我们重点来关注
XADataSourceFactory.build(databaseType, dataSource)
,从名字我们就可以看出,这应该是返回JTA规范里面的XADataSourc
,在ShardingSphere里面很多的功能,可以从代码风格的命名上就能猜出来,这就是优雅代码(吹一波)。不多逼逼,我们进入该方法。
public final class XADataSourceFactory {
public static XADataSource build(final DatabaseType databaseType, final DataSource dataSource) {
return new DataSourceSwapper(XADataSourceDefinitionFactory.getXADataSourceDefinition(databaseType)).swap(dataSource);
}
}
-
首先又是一个SPI定义的
XADataSourceDefinitionFactory
,它根据不同的数据库类型,来加载不同的方言。然后我们进入swap
方法。
public XADataSource swap(final DataSource dataSource) {
XADataSource result = createXADataSource();
setProperties(result, getDatabaseAccessConfiguration(dataSource));
return result;
}
-
很简明,第一步创建,
XADataSource
,第二步给它设置属性(包含数据的连接,用户名密码等),然后返回。 -
返回
XATransactionDataSource
类,关注xaTransactionManager.registerRecoveryResource(resourceName, xaDataSource);
从名字可以看出,这是注册事务恢复资源。这个我们在事务恢复的时候详解。 -
返回
XAShardingTransactionManager.init()
,我们重点来关注:xaTransactionManager.init();
,最后进入AtomikosTransactionManager.init()
public final class AtomikosTransactionManager implements XATransactionManager {
private final UserTransactionManager transactionManager = new UserTransactionManager();
private final UserTransactionService userTransactionService = new UserTransactionServiceImp();
@Override
public void init() {
userTransactionService.init();
}
}
-
进入
UserTransactionServiceImp.init()
private void initialize() {
//添加恢复资源 不用关心
for (RecoverableResource resource : resources_) {
Configuration.addResource ( resource );
}
for (LogAdministrator logAdministrator : logAdministrators_) {
Configuration.addLogAdministrator ( logAdministrator );
}
//注册插件 不用关心
for (TransactionServicePlugin nxt : tsListeners_) {
Configuration.registerTransactionServicePlugin ( nxt );
}
//获取配置属性 重点关心
ConfigProperties configProps = Configuration.getConfigProperties();
configProps.applyUserSpecificProperties(properties_);
//进行初始化
Configuration.init();
}
-
我们重点关注,获取配置属性。最后进入
com.atomikos.icatch.provider.imp.AssemblerImp.initializeProperties()
方法
@Override
public ConfigProperties initializeProperties() {
//读取classpath下的默认配置transactions-defaults.properties
Properties defaults = new Properties();
loadPropertiesFromClasspath(defaults, DEFAULT_PROPERTIES_FILE_NAME);
//读取classpath下,transactions.properties配置,覆盖transactions-defaults.properties中相同key的值
Properties transactionsProperties = new Properties(defaults);
loadPropertiesFromClasspath(transactionsProperties, TRANSACTIONS_PROPERTIES_FILE_NAME);
//读取classpath下,jta.properties,覆盖transactions-defaults.properties、transactions.properties中相同key的值
Properties jtaProperties = new Properties(transactionsProperties);
loadPropertiesFromClasspath(jtaProperties, JTA_PROPERTIES_FILE_NAME);
//读取通过java -Dcom.atomikos.icatch.file方式指定的自定义配置文件路径,覆盖之前的同名配置
Properties customProperties = new Properties(jtaProperties);
loadPropertiesFromCustomFilePath(customProperties);
//最终构造一个ConfigProperties对象,来表示实际要使用的配置
Properties finalProperties = new Properties(customProperties);
return new ConfigProperties(finalProperties);
}
-
接下来重点关注,
Configuration.init()
, 进行初始化。
ublic static synchronized boolean init() {
boolean startupInitiated = false;
if (service_ == null) {
startupInitiated = true;
//SPI方式加载插件注册,无需过多关心
addAllTransactionServicePluginServicesFromClasspath();
ConfigProperties configProperties = getConfigProperties();
//调用插件的beforeInit方法进行初始化话,无需过多关心
notifyBeforeInit(configProperties);
//进行事务日志恢复的初始化,很重要,接下来详解
assembleSystemComponents(configProperties);
//进入系统注解的初始化,一般重要
initializeSystemComponents(configProperties);
notifyAfterInit();
if (configProperties.getForceShutdownOnVmExit()) {
addShutdownHook(new ForceShutdownHook());
}
}
return startupInitiated;
}
-
我们先来关注
assembleSystemComponents(configProperties);
进入它,进入com.atomikos.icatch.provider.imp.AssemblerImp.assembleTransactionService()
方法:
@Override
public TransactionServiceProvider assembleTransactionService(
ConfigProperties configProperties) {
RecoveryLog recoveryLog =null;
//打印日志
logProperties(configProperties.getCompletedProperties());
//生成唯一名字
String tmUniqueName =