
Shardingsphere整合Narayana对XA分布式事务的支持(4)
Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款相互独立,却又能够混合部署配合使用的产品组成。它们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。
ShardingSphere 已于2020年4月16日成为 Apache 软件基金会的顶级项目。
Narayana简单介绍
Narayana(https://narayana.io/),是由Jboss团队提供的XA分布式事务的解决方案。
它具有以下特点:
- 标准的基于JTA实现。
- TransactionManager(TM) 完全去中心化设计,与业务耦合,无需单独部署。
- 事务日志支持数据库存储,支持集群模式下的事务恢复。
ShardingTransactionManager初始化XATransactionDataSource流程
ShardingSphere对XA的支持提供一整套的SPI接口,在初始化话的时候,根据事务类型,先进行TransactionManager的初始化。我们先进入org.apache.shardingsphere.transaction.xa.XAShardingTransactionManager
。代码如下:
- 首先会根据配置的datasource将其转换成XATransactionDataSource,具体代码在
new XATransactionDataSource(databaseType, each.getUniqueResourceName(), each.getDataSource(), xaTransactionManager))
。我们跟进去,代码如下:
- 我们重点来关注
XADataSourceFactory.build(databaseType, dataSource)
,从名字我们就可以看出,这应该是返回JTA规范里面的XADataSource
,在ShardingSphere里面很多的功能,可以从代码风格的命名上就能猜出来,这就是优雅代码(吹一波)。不多逼逼,我们进入该方法。
- 首先又是一个SPI定义的
XADataSourceDefinitionFactory
,它根据不同的数据库类型,来加载不同的方言。然后我们进入swap
方法。
- 很简明,第一步创建,
XADataSource
,第二步给它设置属性(包含数据的连接,用户名密码等),然后返回。
Narayana 初始化过程详解
我们首先进入org.apache.shardingsphere.transaction.xa.narayana.manager.NarayanaXATransactionManager
- 首先我们关注
jtaPropertyManager.getJTAEnvironmentBean().getTransactionManager()
获取TransactionManager,这是整个 Narayana初始化的核心。进入代码com.arjuna.common.internal.util.propertyservice.BeanPopulator.getNamedInstance()
。
- 我们重点关注
Properties defaultProperties = PropertiesFactory.getDefaultProperties();
。最后会进入com.arjuna.common.util.propertyservice.AbstractPropertiesFactory.getPropertiesFromFile()
。
- 加载文件名称为
jbossts-properties.xml
, 加载路径优先级别为 :user.dir > user.home >java.home >classpath。最后再叠加上系统属性,然后返回。
我们再来看一下 jbossts-properties.xml的参考格式如下:
它被视为标准java.util.Properties文件的XML格式并按需加载。entry名称的形式为:类名.属性名
。提供的配置类都在com.arjuna.ats.arjuna.common
包下,以bean结尾的实体类。
- 文件加载后,它会被缓存,直到JVM重新启动才重新读取。对属性文件的更改需要重新启动才能生效
- 在属性加载之后,将检查EnvironmentBean,对于每个字段,如果属性在搜索顺序中包含如下匹配的键,则使用属性的值调用该字段的setter方法,或者使用不同的系统属性调用该字段的setter方法。
- 然后将bean返回给调用者,调用者可以通过调用setter方法进一步覆盖值。
我们返回主线:现在已经加载了配置。接下来就是执行configureFromProperties(bean, name, defaultProperties);
。就是利用反射机制初始化对象,以及给对象的属性赋值。代码如下:
我们在回到 NarayanaXATransactionManager
,分析 XARecoveryModule.getRegisteredXARecoveryModule();
代码如下 :
- 重点关注获取
RecoveryManager.manager();
, 最后会进入com.arjuna.ats.internal.arjuna.recovery.RecoveryManagerImple
的构造方法,代码如下:
- 重点关注
new PeriodicRecovery(threaded, useListener);
,会进行恢复模块的加载,最后会进入com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule
的构造方法。
-
StoreManager.getRecoveryStore();
,最后会进入com.arjuna.ats.arjuna.objectstore.StoreManager.initStore()
,进入事务日志的初始化。代码如下:
- 整个方法是比较清楚的,首先获取事务日志存储的类型(默认使用file模式),然后进行SPI初始化加载,最后再初始化。
- storeType 这里如果配置的是
com.arjuna.ats.internal.arjuna.objectstore.jdbc.JDBCStore
,那么就会进入这个类的构造方法,来进行初始化。代码如下:
- 这个方法还是比较清晰的,根据我们的jdbc的配置,首先初始化连接信息。然后获取连接,然后根据不同的数据库类型,来进行初始化。我们来关心下
_theImple.initialise(jdbcAccess, tableName, jdbcStoreEnvironmentBean);
。代码如下:
- 框架会自动的创建事务日志表来进行存储,所以我们不需要手动创建,也不要惊讶这个表是从哪里来的。创建的表的代码如下:
- 我们在回到主线
PeriodicRecovery
,这个类是继承Thread,调用start就会执行run方法,他会对控制需要进行恢复的事务线程,真的当前的事务状态进行处理,到底是阻塞,还是唤醒。
- 初始化流程中,还有一步是进行事务恢复的,这个我们在后续的章节,单独拿出来进行讲解。
NarayanaXA分布式事务begin流程
我们知道,本地的事务,都会有一个 trainsaction.begin
, 对应XA分布式事务来说也不另外,我们再把思路切换回XAShardingTransactionManager.begin()
, 会调用com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.begin()
方法。代码如下:
- 初始化流程主要就是检查事务状态,获取超时时间,最后也是最重要的创建事务实现。
new TransactionImple(v)
。我们进入该类的构造方法,代码如下:
- 这里面最重要是2步,第一步是初始化 AtomicAction,第二步是 AtomicAction.begin()。我们先来看
new AtomicAction
。会对相关的父类,进行初始化。AtomicAction的继承体系图为:
● 我们接下来看com.arjuna.ats.arjuna.AtomicAction.begin()
。代码如下:
- 我们先来分析
super.start()
。最后会进入com.arjuna.ats.arjuna.coordinator.BasicAction.begin()
。代码如下:
XATransactionDataSource getConnection() 流程
我们都知道想要执行SQL语句,必须要获取到数据库的connection。让我们再回到 XAShardingTransactionManager.getConnection()
最后会调用到org.apache.shardingsphere.transaction.xa.jta.datasourceXATransactionDataSource.getConnection()
。流程图如下:
代码 :
- 首先第一步很关心,尤其是对shardingsphere来说,因为在一个事务里面,会有多个SQL语句,打到相同的数据库,所以对相同的数据库,必须获取同一个XAConnection,这样才能进行XA事务的提交与回滚。
- 我们接下来关心
transaction.enlistResource(new SingleXAResource(resourceName, xaConnection.getXAResource()));
, 会进入com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImp.enlistResource()
, 代码太长,截取一部分。
- 哦多尅,看见了吗,各位,看见了
xaRes.start(xid, xaStartNormal);
了吗????,我们进去,假设我们使用的Mysql数据库:
- 组装
XA start Xid
SQL语句,进行执行。
到这里,我们总结下,在获取数据库连接的时候,我们执行了XA协议接口中的 XA start xid
Narayana commit流程源码分析
我们进入com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit()
方法,代码如下:
- 我们重点来关注
theTransaction.commitAndDisassociate();
,最后进入com.arjuna.ats.arjuna.AtomicAction.commit()
代码如下:
- 最后我们会进入
com.arjuna.ats.arjuna.coordinator.BasicAction.End()
方法,会首先判断是否能优化成一阶段提交,否则进行二阶段提交(二阶段提交还可以使用异步线程池方式)。代码如下:
一阶段提交
进入方法 onePhaseCommit
, 最后会调用com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord.topLevelOnePhaseCommit()
。该方法首先会发起 XA end 语句,然后再执行XA commit语句。代码如下:
二阶段提交
- 首先会进行进入
prepare(reportHeuristics);
, 最后会调用com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord.topLevelPrepare()
该方法首先会执行 XA end 语句,然后执行 XA prepare语句。代码如下:
- 接下来进行提交,进入方法
phase2Commit
, 最后会调用com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord.topLevelCommit()
。该方法会执行XA commit语句。代码如下:
Narayana 回滚流程
首先我们先切换回org.apache.shardingsphere.transaction.xa.XAShardingTransactionManager.rollback()
方法,然后会进入 com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.rollback()
方法,代码如下:
- 代码最后后进入
com.arjuna.ats.arjuna.coordinator.BasicAction.topLevelAbort()
。代码如下:
- 接下来就是清除换成,清除事务日志。代码如下:
总结:可以看到回滚流程会稍微毕竟简单。先执行XA end语句,然后执行XA rollback语句。
文章到此,已经写的很长很多了,我们分析了ShardingSphere对于XA方案,提供了一套SPI解决方案,对Narayana进行了整合,也分析了Narayana初始化流程,开始事务流程,获取连接流程,提交事务流程,回滚事务流程。下一篇文章,我们来详解narayana的事务恢复流程。
关于我们
Apache ShardingSphere不断践行Apache Way,致力于打造充满活力、规范、互助的社区!开源路上,我们欢迎你的加入。
项目地址:
https://github.com/apache/shardingsphere
更多信息请浏览官网:
https://shardingsphere.apache.org/
作者介绍:肖宇,Apache ShardingSphere Committer,开源hmily分布式事务框架作者,
开源soul网关作者,热爱开源,追求写优雅代码。目前就职于京东数科,参与ShardingSphere的开源建设,以及分布式数据库的研发工作。
