mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-30 07:06:42 +08:00
561 lines
36 KiB
HTML
561 lines
36 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
<link rel="icon" href="/static/favicon.png">
|
||
<title>29 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(下).md.html</title>
|
||
<!-- Spectre.css framework -->
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
<!-- theme css & js -->
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
</head>
|
||
<body>
|
||
<div class="book-container">
|
||
<div class="book-sidebar">
|
||
<div class="book-brand">
|
||
<a href="/">
|
||
<img src="/static/favicon.png">
|
||
<span>技术文章摘抄</span>
|
||
</a>
|
||
</div>
|
||
<div class="book-menu uncollapsible">
|
||
<ul class="uncollapsible">
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li><a href="../">上一级</a></li>
|
||
</ul>
|
||
<ul class="uncollapsible">
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/00 如何正确学习一款分库分表开源框架?.md.html">00 如何正确学习一款分库分表开源框架?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/01 从理论到实践:如何让分库分表真正落地?.md.html">01 从理论到实践:如何让分库分表真正落地?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/02 顶级项目:ShardingSphere 是一款什么样的 Apache 开源软件?.md.html">02 顶级项目:ShardingSphere 是一款什么样的 Apache 开源软件?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/03 规范兼容:JDBC 规范与 ShardingSphere 是什么关系?.md.html">03 规范兼容:JDBC 规范与 ShardingSphere 是什么关系?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/04 应用集成:在业务系统中使用 ShardingSphere 的方式有哪些?.md.html">04 应用集成:在业务系统中使用 ShardingSphere 的方式有哪些?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/05 配置驱动:ShardingSphere 中的配置体系是如何设计的?.md.html">05 配置驱动:ShardingSphere 中的配置体系是如何设计的?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/06 数据分片:如何实现分库、分表、分库+分表以及强制路由?(上).md.html">06 数据分片:如何实现分库、分表、分库+分表以及强制路由?(上)</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/07 数据分片:如何实现分库、分表、分库+分表以及强制路由?(下).md.html">07 数据分片:如何实现分库、分表、分库+分表以及强制路由?(下)</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/08 读写分离:如何集成分库分表+数据库主从架构?.md.html">08 读写分离:如何集成分库分表+数据库主从架构?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/09 分布式事务:如何使用强一致性事务与柔性事务?.md.html">09 分布式事务:如何使用强一致性事务与柔性事务?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/10 数据脱敏:如何确保敏感数据的安全访问?.md.html">10 数据脱敏:如何确保敏感数据的安全访问?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/11 编排治理:如何实现分布式环境下的动态配置管理?.md.html">11 编排治理:如何实现分布式环境下的动态配置管理?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/12 从应用到原理:如何高效阅读 ShardingSphere 源码?.md.html">12 从应用到原理:如何高效阅读 ShardingSphere 源码?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/13 微内核架构:ShardingSphere 如何实现系统的扩展性?.md.html">13 微内核架构:ShardingSphere 如何实现系统的扩展性?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/14 分布式主键:ShardingSphere 中有哪些分布式主键实现方式?.md.html">14 分布式主键:ShardingSphere 中有哪些分布式主键实现方式?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/15 解析引擎:SQL 解析流程应该包括哪些核心阶段?(上).md.html">15 解析引擎:SQL 解析流程应该包括哪些核心阶段?(上)</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/16 解析引擎:SQL 解析流程应该包括哪些核心阶段?(下).md.html">16 解析引擎:SQL 解析流程应该包括哪些核心阶段?(下)</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/17 路由引擎:如何理解分片路由核心类 ShardingRouter 的运作机制?.md.html">17 路由引擎:如何理解分片路由核心类 ShardingRouter 的运作机制?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/18 路由引擎:如何实现数据访问的分片路由和广播路由?.md.html">18 路由引擎:如何实现数据访问的分片路由和广播路由?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/19 路由引擎:如何在路由过程中集成多种路由策略和路由算法?.md.html">19 路由引擎:如何在路由过程中集成多种路由策略和路由算法?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/20 改写引擎:如何理解装饰器模式下的 SQL 改写实现机制?.md.html">20 改写引擎:如何理解装饰器模式下的 SQL 改写实现机制?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/21 执行引擎:分片环境下 SQL 执行的整体流程应该如何进行抽象?.md.html">21 执行引擎:分片环境下 SQL 执行的整体流程应该如何进行抽象?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/22 执行引擎:如何把握 ShardingSphere 中的 Executor 执行模型?(上).md.html">22 执行引擎:如何把握 ShardingSphere 中的 Executor 执行模型?(上)</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/23 执行引擎:如何把握 ShardingSphere 中的 Executor 执行模型?(下).md.html">23 执行引擎:如何把握 ShardingSphere 中的 Executor 执行模型?(下)</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/24 归并引擎:如何理解数据归并的类型以及简单归并策略的实现过程?.md.html">24 归并引擎:如何理解数据归并的类型以及简单归并策略的实现过程?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/25 归并引擎:如何理解流式归并和内存归并在复杂归并场景下的应用方式?.md.html">25 归并引擎:如何理解流式归并和内存归并在复杂归并场景下的应用方式?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/26 读写分离:普通主从架构和分片主从架构分别是如何实现的?.md.html">26 读写分离:普通主从架构和分片主从架构分别是如何实现的?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/27 分布式事务:如何理解 ShardingSphere 中对分布式事务的抽象过程?.md.html">27 分布式事务:如何理解 ShardingSphere 中对分布式事务的抽象过程?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/28 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(上).md.html">28 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(上)</a>
|
||
</li>
|
||
<li>
|
||
<a class="current-tab" href="/专栏/ShardingSphere 核心原理精讲-完/29 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(下).md.html">29 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(下)</a>
|
||
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/30 数据脱敏:如何基于改写引擎实现低侵入性数据脱敏方案?.md.html">30 数据脱敏:如何基于改写引擎实现低侵入性数据脱敏方案?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/31 配置中心:如何基于配置中心实现配置信息的动态化管理?.md.html">31 配置中心:如何基于配置中心实现配置信息的动态化管理?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/32 注册中心:如何基于注册中心实现数据库访问熔断机制?.md.html">32 注册中心:如何基于注册中心实现数据库访问熔断机制?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/33 链路跟踪:如何基于 Hook 机制以及 OpenTracing 协议实现数据访问链路跟踪?.md.html">33 链路跟踪:如何基于 Hook 机制以及 OpenTracing 协议实现数据访问链路跟踪?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/34 系统集成:如何完成 ShardingSphere 内核与 Spring+SpringBoot 的无缝整合?.md.html">34 系统集成:如何完成 ShardingSphere 内核与 Spring+SpringBoot 的无缝整合?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/35 结语:ShardingSphere 总结及展望.md.html">35 结语:ShardingSphere 总结及展望</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
<div class="sidebar-toggle-inner"></div>
|
||
</div>
|
||
<script>
|
||
function add_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.add('show')
|
||
}
|
||
function remove_inner() {
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
inner.classList.remove('show')
|
||
}
|
||
function sidebar_toggle() {
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let content = document.querySelector('.off-canvas-content')
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
sidebar_toggle.classList.remove('extend')
|
||
sidebar.classList.remove('hide')
|
||
content.classList.remove('extend')
|
||
} else { // hide
|
||
sidebar_toggle.classList.add('extend')
|
||
sidebar.classList.add('hide')
|
||
content.classList.add('extend')
|
||
}
|
||
}
|
||
function open_sidebar() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.add('show')
|
||
overlay.classList.add('show')
|
||
}
|
||
function hide_canvas() {
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
sidebar.classList.remove('show')
|
||
overlay.classList.remove('show')
|
||
}
|
||
</script>
|
||
<div class="off-canvas-content">
|
||
<div class="columns">
|
||
<div class="column col-12 col-lg-12">
|
||
<div class="book-navbar">
|
||
<!-- For Responsive Layout -->
|
||
<header class="navbar">
|
||
<section class="navbar-section">
|
||
<a onclick="open_sidebar()">
|
||
<i class="icon icon-menu"></i>
|
||
</a>
|
||
</section>
|
||
</header>
|
||
</div>
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;">
|
||
<div class="book-post">
|
||
<p id="tip" align="center"></p>
|
||
<div><h1>29 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(下)</h1>
|
||
<p>在上一课时中,我们针对 ShardingSphere 中支持强一致性事务的 XAShardingTransactionManager 的部分内容进行了详细的展开,今天我们继续讲解该类的剩余内容,同时也会介绍支持柔性事务的 SeataATShardingTransactionManager。</p>
|
||
<h3>XAShardingTransactionManager</h3>
|
||
<p>关于 XAShardingTransactionManager,上一讲中我们介绍了 XADataSource、XAConnection 和 XATransactionDataSource 等核心类。</p>
|
||
<p>接下来,我们在上一讲的基础上给出 XATransactionManager 和 ShardingConnection 类的实现过程。</p>
|
||
<h4>1.XATransactionManager</h4>
|
||
<p>让我们先回到 XAShardingTransactionManager。我们已经在前面介绍了 XAShardingTransactionManager 中的变量,接下来看一下它所实现的方法,首先是如下所示的 init 方法:</p>
|
||
<pre><code>public void init(final DatabaseType databaseType, final Collection<ResourceDataSource> resourceDataSources) {
|
||
for (ResourceDataSource each : resourceDataSources) {
|
||
//创建XATransactionDataSource并进行缓存
|
||
cachedDataSources.put(each.getOriginalName(), new XATransactionDataSource(databaseType, each.getUniqueResourceName(), each.getDataSource(), xaTransactionManager));
|
||
}
|
||
//初始化XATransactionManager
|
||
xaTransactionManager.init();
|
||
}
|
||
</code></pre>
|
||
<p>上述方法根据传入的 ResourceDataSource 构建了 XATransactionDataSource 并放入缓存中,同时对通过 SPI 机制创建的 XATransactionManager 也执行了它的 init 方法进行初始化。</p>
|
||
<p>XAShardingTransactionManager 的 getTransactionType、isInTransaction 和 getConnection 方法都比较简单,如下所示:</p>
|
||
<pre><code>@Override
|
||
public TransactionType getTransactionType() {
|
||
return TransactionType.XA;
|
||
}
|
||
@Override
|
||
public boolean isInTransaction() {
|
||
return Status.STATUS_NO_TRANSACTION != xaTransactionManager.getTransactionManager().getStatus();
|
||
}
|
||
|
||
@Override
|
||
public Connection getConnection(final String dataSourceName) throws SQLException {
|
||
try {
|
||
return cachedDataSources.get(dataSourceName).getConnection();
|
||
} catch (final SystemException | RollbackException ex) {
|
||
throw new SQLException(ex);
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>而与事务操作相关的 begin、commit 和 rollback 方法的实现同样比较简单,都是直接委托保存在 XATransactionManager 中的 TransactionManager 进行完成,如下所示:</p>
|
||
<pre><code>@Override
|
||
public void begin() {
|
||
xaTransactionManager.getTransactionManager().begin();
|
||
}
|
||
@Override
|
||
public void commit() {
|
||
xaTransactionManager.getTransactionManager().commit();
|
||
}
|
||
@Override
|
||
public void rollback() {
|
||
xaTransactionManager.getTransactionManager().rollback();
|
||
}
|
||
</code></pre>
|
||
<p>至此,sharding-transaction-xa-core 工程中的所有内容都已经介绍完毕。让我们转到 sharding-transaction-xa-atomikos-manager 工程,看看 AtomikosTransactionManager 的实现,这也是 ShardingSphere 中关于 TransactionManager 的默认实现。</p>
|
||
<p>而在此之前,让我们先来看一下代表资源的 AtomikosXARecoverableResource 的实现,如下所示:</p>
|
||
<pre><code>public final class AtomikosXARecoverableResource extends JdbcTransactionalResource {
|
||
private final String resourceName;
|
||
AtomikosXARecoverableResource(final String serverName, final XADataSource xaDataSource) {
|
||
super(serverName, xaDataSource);
|
||
resourceName = serverName;
|
||
}
|
||
@Override
|
||
public boolean usesXAResource(final XAResource xaResource) {
|
||
return resourceName.equals(((SingleXAResource) xaResource).getResourceName());
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>可以看到,这里的 usesXAResource 方法实际上就是通过基于对 SingleXAResource 的 ResourceName 进行比对来确定是否在使用资源,这也是为什么要设计包装了 XAResource 的 SingleXAResource 类的原因。</p>
|
||
<p>AtomikosTransactionManager 中使用了 AtomikosXARecoverableResource,其实现过程如下所示:</p>
|
||
<pre><code>public final class AtomikosTransactionManager implements XATransactionManager {
|
||
private final UserTransactionManager transactionManager = new UserTransactionManager();
|
||
private final UserTransactionService userTransactionService = new UserTransactionServiceImp();
|
||
@Override
|
||
public void init() {
|
||
userTransactionService.init();
|
||
}
|
||
@Override
|
||
public void registerRecoveryResource(final String dataSourceName, final XADataSource xaDataSource) {
|
||
userTransactionService.registerResource(new AtomikosXARecoverableResource(dataSourceName, xaDataSource));
|
||
}
|
||
@Override
|
||
public void removeRecoveryResource(final String dataSourceName, final XADataSource xaDataSource) {
|
||
userTransactionService.removeResource(new AtomikosXARecoverableResource(dataSourceName, xaDataSource));
|
||
}
|
||
@Override
|
||
@SneakyThrows
|
||
public void enlistResource(final SingleXAResource xaResource) {
|
||
transactionManager.getTransaction().enlistResource(xaResource);
|
||
}
|
||
@Override
|
||
public TransactionManager getTransactionManager() {
|
||
return transactionManager;
|
||
}
|
||
@Override
|
||
public void close() {
|
||
userTransactionService.shutdown(true);
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>上述方法本质上都是对 Atomikos 的 UserTransactionManager 和 UserTransactionService 的简单调用,注意到 Atomikos 的 UserTransactionManager 实现了 TransactionManager 接口,封装了所有 TransactionManager 需要完成的工作。</p>
|
||
<p>看完 sharding-transaction-xa-atomikos-manager 工程之后,我们来到另一个 sharding-transaction-xa-bitronix-manager 工程,该工程提供了基于 bitronix 的 XATransactionManager 实现方案,即 BitronixXATransactionManager 类:</p>
|
||
<pre><code>public final class BitronixXATransactionManager implements XATransactionManager {
|
||
private final BitronixTransactionManager bitronixTransactionManager = TransactionManagerServices.getTransactionManager();
|
||
@Override
|
||
public void init() {
|
||
}
|
||
@SneakyThrows
|
||
@Override
|
||
public void registerRecoveryResource(final String dataSourceName, final XADataSource xaDataSource) {
|
||
ResourceRegistrar.register(new BitronixRecoveryResource(dataSourceName, xaDataSource));
|
||
}
|
||
@SneakyThrows
|
||
@Override
|
||
public void removeRecoveryResource(final String dataSourceName, final XADataSource xaDataSource) {
|
||
ResourceRegistrar.unregister(new BitronixRecoveryResource(dataSourceName, xaDataSource));
|
||
}
|
||
@SneakyThrows
|
||
@Override
|
||
public void enlistResource(final SingleXAResource singleXAResource) {
|
||
bitronixTransactionManager.getTransaction().enlistResource(singleXAResource);
|
||
}
|
||
@Override
|
||
public TransactionManager getTransactionManager() {
|
||
return bitronixTransactionManager;
|
||
}
|
||
@Override
|
||
public void close() {
|
||
bitronixTransactionManager.shutdown();
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>对上述代码的理解也依赖与对 bitronix 框架的熟悉程度,整个封装过程简单明了。我们无意对 bitronix 框架做过多展开,而是更多关注于 ShardingSphere 中对 XATransactionManager 的抽象过程。</p>
|
||
<p>作为总结,我们在上一课时的基础上,进一步梳理了 XA 两阶段提交相关的核心类之间的关系,如下图所示:</p>
|
||
<p><img src="assets/CgqCHl9oXe6AK8JkAAByEfwPBs0489.png" alt="png" /></p>
|
||
<h4>2.ShardingConnection</h4>
|
||
<p>上图展示了整个流程的源头是在 ShardingConnection 类。我们在 ShardingConnection 的构造函数中发现了创建 ShardingTransactionManager 的过程,如下所示:</p>
|
||
<pre><code>shardingTransactionManager = runtimeContext.getShardingTransactionManagerEngine().getTransactionManager(transactionType);
|
||
</code></pre>
|
||
<p>在 ShardingConnection 的多处代码中都用到了上面所创建的 shardingTransactionManager 对象。例如,用于获取连接的 createConnection 方法:</p>
|
||
<pre><code>@Override
|
||
protected Connection createConnection(final String dataSourceName, final DataSource dataSource) throws SQLException {
|
||
return isInShardingTransaction() ? shardingTransactionManager.getConnection(dataSourceName) : dataSource.getConnection();
|
||
}
|
||
</code></pre>
|
||
<p>用于判断是否是在同一个事务中的 isInShardingTransaction 方法:</p>
|
||
<pre><code>private boolean isInShardingTransaction() {
|
||
return null != shardingTransactionManager && shardingTransactionManager.isInTransaction();
|
||
}
|
||
</code></pre>
|
||
<p>以及如下所示的 setAutoCommit 方法完成了对 autoCommit 的处理:</p>
|
||
<pre><code>@Override
|
||
public void setAutoCommit(final boolean autoCommit) throws SQLException {
|
||
if (TransactionType.LOCAL == transactionType) {
|
||
super.setAutoCommit(autoCommit);
|
||
return;
|
||
}
|
||
if (autoCommit && !shardingTransactionManager.isInTransaction() || !autoCommit && shardingTransactionManager.isInTransaction()) {
|
||
return;
|
||
}
|
||
if (autoCommit && shardingTransactionManager.isInTransaction()) {
|
||
shardingTransactionManager.commit();
|
||
return;
|
||
}
|
||
if (!autoCommit && !shardingTransactionManager.isInTransaction()) {
|
||
closeCachedConnections();
|
||
shardingTransactionManager.begin();
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>在上述方法中,可以看到当事务类型为本地事务时,直接调用 ShardingConnection 的父类 AbstractConnectionAdapter 中的 setAutoCommit 方法完成本地事务的自动提交处理。</p>
|
||
<p>而当 autoCommit 为 true 且运行在事务中时,会调用 shardingTransactionManager.commit() 方法完成提交;而当 autoCommit 为 false 且当前不在事务中时,会调用 shardingTransactionManager.begin() 方法启动事务。</p>
|
||
<p>最后的 commit 和 rollback 的处理方式与 setAutoCommit 类似,都是根据事务类型来决定是否要进行分布式提交和回滚,如下所示:</p>
|
||
<pre><code>@Override
|
||
public void commit() throws SQLException {
|
||
if (TransactionType.LOCAL == transactionType) {
|
||
super.commit();
|
||
} else {
|
||
shardingTransactionManager.commit();
|
||
}
|
||
}
|
||
@Override
|
||
public void rollback() throws SQLException {
|
||
if (TransactionType.LOCAL == transactionType) {
|
||
super.rollback();
|
||
} else {
|
||
shardingTransactionManager.rollback();
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>我们在上一课时中提到,ShardingSphere 在提供了两阶段提交的 XA 协议实现方案的同时,也实现了柔性事务。</p>
|
||
<p>在介绍完 XAShardingTransactionManager 之后,我们继续来看基于 Seata 框架的柔性事务 TransactionManager 实现类 SeataATShardingTransactionManager。</p>
|
||
<h3>SeataATShardingTransactionManager</h3>
|
||
<p>因为 SeataATShardingTransactionManager 完全采用阿里巴巴的 Seata 框架来提供分布式事务特性,而不是遵循类似 XA 这样的开发规范,所以在代码实现上比 XAShardingTransactionManager 的类层结构要简单很多,把复杂性都屏蔽在了框架的内部。</p>
|
||
<p>要想集成 Seata,我们首先需要初始化 TMClient 和 RMClient 这两个客户端对象,在 Seata 内部,这两个客户端之间会基于 RPC 的方式进行通信。</p>
|
||
<p>所以,ShardingSphere 在 XAShardingTransactionManager 中的 init 方法中实现了一个 initSeataRPCClient 方法来初始化这两个客户端对象,如下所示:</p>
|
||
<pre><code>//根据 seata.conf 配置文件创建配置对象
|
||
private final FileConfiguration configuration = new FileConfiguration("seata.conf");
|
||
private void initSeataRPCClient() {
|
||
String applicationId = configuration.getConfig("client.application.id");
|
||
Preconditions.checkNotNull(applicationId, "please config application id within seata.conf file");
|
||
String transactionServiceGroup = configuration.getConfig("client.transaction.service.group", "default");
|
||
TMClient.init(applicationId, transactionServiceGroup);
|
||
RMClient.init(applicationId, transactionServiceGroup);
|
||
}
|
||
</code></pre>
|
||
<p>回想我们在“09 | 分布式事务:如何使用强一致事务与柔性事务?”中关于 Seata 使用方式的介绍,不难理解这里通过 seata.conf 配置文件中所配置的 application.id 和 transaction.service.group 这两个配置项来执行初始化操作。</p>
|
||
<p>同时,对于 Seata 而言,它也提供了一套构建在 JDBC 规范之上的实现策略,这点和“03 | 规范兼容:JDBC 规范与 ShardingSphere 是什么关系?”中介绍的 ShardingSphere 与 JDBC 规范之间的兼容性类似。</p>
|
||
<p>而在命名上,Seata 更为直接明了,使用 DataSourceProxy 和 ConnectionProxy 这种代理对象。以 DataSourceProxy 为例,我们可以梳理它的类层结构如下:</p>
|
||
<p><img src="assets/CgqCHl9oXgKACi15AAA7sb7XKlo735.png" alt="png" /></p>
|
||
<p>可以看到 DataSourceProxy 实现了自己定义的 Resource 接口,然后继承了抽象类 AbstractDataSourceProxy,而后者则实现了 JDBC 中的 DataSource 接口。</p>
|
||
<p>所以,在我们初始化 Seata 框架时,同样需要根据输入的 DataSource 对象来构建 DataSourceProxy,并通过 DataSourceProxy 获取 ConnectionProxy。SeataATShardingTransactionManager 类中的相关代码如下所示:</p>
|
||
<pre><code>@Override
|
||
public void init(final DatabaseType databaseType, final Collection<ResourceDataSource> resourceDataSources) {
|
||
//初始化 Seata 客户端
|
||
initSeataRPCClient();
|
||
//创建 DataSourceProxy 并放入到 Map 中
|
||
for (ResourceDataSource each : resourceDataSources) {
|
||
dataSourceMap.put(each.getOriginalName(), new DataSourceProxy(each.getDataSource()));
|
||
}
|
||
}
|
||
@Override
|
||
public Connection getConnection(final String dataSourceName) throws SQLException {
|
||
//根据 DataSourceProxy 获取 ConnectionProxy
|
||
return dataSourceMap.get(dataSourceName).getConnection();
|
||
}
|
||
</code></pre>
|
||
<p>介绍完初始化工作之后,我们来看 SeataATShardingTransactionManager 中提供了事务开启和提交相关的入口。在 Seata 中,GlobalTransaction 是一个核心接口,封装了面向用户操作层的分布式事务访问入口,该接口的定义如下所示,可以从方法命名上直接看出对应的操作含义:</p>
|
||
<pre><code>public interface GlobalTransaction {
|
||
void begin() throws TransactionException;
|
||
void begin(int timeout) throws TransactionException;
|
||
void begin(int timeout, String name) throws TransactionException;
|
||
void commit() throws TransactionException;
|
||
void rollback() throws TransactionException;
|
||
GlobalStatus getStatus() throws TransactionException;
|
||
String getXid();
|
||
}
|
||
</code></pre>
|
||
<p>ShardingSphere 作为 GlobalTransaction 的用户层,同样基于 GlobalTransaction 接口来完成分布式事务操作。但 ShardingSphere 并没有直接使用这一层,而是设计了一个 SeataTransactionHolder 类,保存着线程安全的 GlobalTransaction 对象。</p>
|
||
<p>SeataTransactionHolder 类位于 sharding-transaction-base-seata-at 工程中,定义如下:</p>
|
||
<pre><code>final class SeataTransactionHolder {
|
||
private static final ThreadLocal<GlobalTransaction> CONTEXT = new ThreadLocal<>();
|
||
static void set(final GlobalTransaction transaction) {
|
||
CONTEXT.set(transaction);
|
||
}
|
||
static GlobalTransaction get() {
|
||
return CONTEXT.get();
|
||
}
|
||
static void clear() {
|
||
CONTEXT.remove();
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>可以看到这里使用了 ThreadLocal 工具类来确保对 GlobalTransaction 访问的线程安全性。</p>
|
||
<p>接下来的问题是,如何判断当前操作是否处于一个全局事务中呢?</p>
|
||
<p>在 Seata 中,存在一个上下文对象 RootContex,该类就是用来保存参与者和发起者之间传播的 Xid。当事务发起者开启全局事务后,会将 Xid 填充到 RootContext 里;然后 Xid 将沿着服务调用链一直传播,进而填充到每个事务参与者进程的 RootContext 里;事务参与者发现 RootContext 中存在 Xid 时,就可以知道自己处于全局事务中。</p>
|
||
<p>基于这层原理,我们只需要采用如下所示的判断方法就能得出是否处于全局事务中的结论:</p>
|
||
<pre><code>@Override
|
||
public boolean isInTransaction() {
|
||
return null != RootContext.getXID();
|
||
}
|
||
</code></pre>
|
||
<p>同时,Seata 也提供了一个针对全局事务的上下文类 GlobalTransactionContext,通过这个上下文类,我们可以使用 getCurrent 方法来获取一个 GlobalTransaction对象,或者通过 getCurrentOrCreate 方法在无法获取 GlobalTransaction 对象时新建一个。</p>
|
||
<p>讲到这里,我们就不难理解 SeataATShardingTransactionManager 中 begin 方法的实现过程了,如下所示:</p>
|
||
<pre><code>@Override
|
||
@SneakyThrows
|
||
public void begin() {
|
||
SeataTransactionHolder.set(GlobalTransactionContext.getCurrentOrCreate());
|
||
SeataTransactionHolder.get().begin();
|
||
SeataTransactionBroadcaster.collectGlobalTxId();
|
||
}
|
||
</code></pre>
|
||
<p>这里通过 GlobalTransactionContext.getCurrentOrCreate() 方法创建了一个 GlobalTransaction,然后将其保存到了 SeataTransactionHolder 中。接着从 SeataTransactionHolder 中获取一个 GlobalTransaction,并调用 begin 方法启动事务。</p>
|
||
<p>注意到这里还有一个 SeataTransactionBroadcaster 类,该类就是用来保存 Seata 全局 Xid 的一个容器类。我们会在事务启动时收集全局 Xid 并进行保存,而在事务提交或回滚时清空这些 Xid。</p>
|
||
<p>所以,如下所示的 commit、rollback 和 close 方法的实现过程就都变得容易理解了:</p>
|
||
<pre><code>@Override
|
||
public void commit() {
|
||
try {
|
||
SeataTransactionHolder.get().commit();
|
||
} finally {
|
||
SeataTransactionBroadcaster.clear();
|
||
SeataTransactionHolder.clear();
|
||
}
|
||
}
|
||
@Override
|
||
public void rollback() {
|
||
try {
|
||
SeataTransactionHolder.get().rollback();
|
||
} finally {
|
||
SeataTransactionBroadcaster.clear();
|
||
SeataTransactionHolder.clear();
|
||
}
|
||
}
|
||
@Override
|
||
public void close() {
|
||
dataSourceMap.clear();
|
||
SeataTransactionHolder.clear();
|
||
TmRpcClient.getInstance().destroy();
|
||
RmRpcClient.getInstance().destroy();
|
||
}
|
||
</code></pre>
|
||
<p>sharding-transaction-base-seata-at 工程中的代码实际上就只有这些内容,这些内容也构成了在 ShardingSphere中 集成 Seata 框架的实现过程。</p>
|
||
<h3>从源码解析到日常开发</h3>
|
||
<p>今天的内容给出了在应用程序中如何集成 Seata 分布式事务框架的详细过程,ShardingSphere 为我们提供了一种模版实现。在日常开发过程中,如果我们想要在业务代码中集成 Seata,就可以参考 SeataTransactionHolder、SeataATShardingTransactionManager 等核心类中的代码,而不需要做太多的修改。</p>
|
||
<h3>小结与预告</h3>
|
||
<p>本课时是 ShardingSphere 分布式事务的最后一讲,我们介绍完了 XAShardingTransactionManager 的剩余部分内容,以及 SeataATShardingTransactionManager 的完整实现。</p>
|
||
<p>回顾上一课时内容,我们发现理解 XAShardingTransactionManager 的难点在于,从 ShardingConnection 到底层 JDBC 规范的整个集成和兼容过程。而对于 XAShardingTransactionManager 而言,我们需要对 Seata 框架本身有一定的了解,才能更好地理解今天的内容。</p>
|
||
<p>这里给你留一道思考题:如果让你实现对 Seata 框架的集成,你需要做哪些核心步骤?欢迎你在留言区与大家讨论,我将逐一点评解答。</p>
|
||
<p>介绍完分布式事务之后,我们将进入“ShardingSphere 中编排治理方面的源码解析”模块,从下一课时开始,我将要介绍数据脱敏模块的实现原理。</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/28 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(上).md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/30 数据脱敏:如何基于改写引擎实现低侵入性数据脱敏方案?.md.html">下一页</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
</div>
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709974e0fe9b3d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
</body>
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag() {
|
||
dataLayer.push(arguments);
|
||
}
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-NPSEEVD756');
|
||
var path = window.location.pathname
|
||
var cookie = getCookie("lastPath");
|
||
console.log(path)
|
||
if (path.replace("/", "") === "") {
|
||
if (cookie.replace("/", "") !== "") {
|
||
console.log(cookie)
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
}
|
||
} else {
|
||
setCookie("lastPath", path)
|
||
}
|
||
function setCookie(cname, cvalue) {
|
||
var d = new Date();
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
var expires = "expires=" + d.toGMTString();
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
}
|
||
function getCookie(cname) {
|
||
var name = cname + "=";
|
||
var ca = document.cookie.split(';');
|
||
for (var i = 0; i < ca.length; i++) {
|
||
var c = ca[i].trim();
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
}
|
||
return "";
|
||
}
|
||
</script>
|
||
</html>
|