mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-26 21:26:41 +08:00
1173 lines
34 KiB
HTML
1173 lines
34 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>27 分布式事务:如何理解 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 如何正确学习一款分库分表开源框架?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/01 从理论到实践:如何让分库分表真正落地?.md.html">01 从理论到实践:如何让分库分表真正落地?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/02 顶级项目:ShardingSphere 是一款什么样的 Apache 开源软件?.md.html">02 顶级项目:ShardingSphere 是一款什么样的 Apache 开源软件?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/03 规范兼容:JDBC 规范与 ShardingSphere 是什么关系?.md.html">03 规范兼容:JDBC 规范与 ShardingSphere 是什么关系?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/04 应用集成:在业务系统中使用 ShardingSphere 的方式有哪些?.md.html">04 应用集成:在业务系统中使用 ShardingSphere 的方式有哪些?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/05 配置驱动:ShardingSphere 中的配置体系是如何设计的?.md.html">05 配置驱动:ShardingSphere 中的配置体系是如何设计的?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/06 数据分片:如何实现分库、分表、分库+分表以及强制路由?(上).md.html">06 数据分片:如何实现分库、分表、分库+分表以及强制路由?(上).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/07 数据分片:如何实现分库、分表、分库+分表以及强制路由?(下).md.html">07 数据分片:如何实现分库、分表、分库+分表以及强制路由?(下).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/08 读写分离:如何集成分库分表+数据库主从架构?.md.html">08 读写分离:如何集成分库分表+数据库主从架构?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/09 分布式事务:如何使用强一致性事务与柔性事务?.md.html">09 分布式事务:如何使用强一致性事务与柔性事务?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/10 数据脱敏:如何确保敏感数据的安全访问?.md.html">10 数据脱敏:如何确保敏感数据的安全访问?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/11 编排治理:如何实现分布式环境下的动态配置管理?.md.html">11 编排治理:如何实现分布式环境下的动态配置管理?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/12 从应用到原理:如何高效阅读 ShardingSphere 源码?.md.html">12 从应用到原理:如何高效阅读 ShardingSphere 源码?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/13 微内核架构:ShardingSphere 如何实现系统的扩展性?.md.html">13 微内核架构:ShardingSphere 如何实现系统的扩展性?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/14 分布式主键:ShardingSphere 中有哪些分布式主键实现方式?.md.html">14 分布式主键:ShardingSphere 中有哪些分布式主键实现方式?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/15 解析引擎:SQL 解析流程应该包括哪些核心阶段?(上).md.html">15 解析引擎:SQL 解析流程应该包括哪些核心阶段?(上).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/16 解析引擎:SQL 解析流程应该包括哪些核心阶段?(下).md.html">16 解析引擎:SQL 解析流程应该包括哪些核心阶段?(下).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/17 路由引擎:如何理解分片路由核心类 ShardingRouter 的运作机制?.md.html">17 路由引擎:如何理解分片路由核心类 ShardingRouter 的运作机制?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/18 路由引擎:如何实现数据访问的分片路由和广播路由?.md.html">18 路由引擎:如何实现数据访问的分片路由和广播路由?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/19 路由引擎:如何在路由过程中集成多种路由策略和路由算法?.md.html">19 路由引擎:如何在路由过程中集成多种路由策略和路由算法?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/20 改写引擎:如何理解装饰器模式下的 SQL 改写实现机制?.md.html">20 改写引擎:如何理解装饰器模式下的 SQL 改写实现机制?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/21 执行引擎:分片环境下 SQL 执行的整体流程应该如何进行抽象?.md.html">21 执行引擎:分片环境下 SQL 执行的整体流程应该如何进行抽象?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/22 执行引擎:如何把握 ShardingSphere 中的 Executor 执行模型?(上).md.html">22 执行引擎:如何把握 ShardingSphere 中的 Executor 执行模型?(上).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/23 执行引擎:如何把握 ShardingSphere 中的 Executor 执行模型?(下).md.html">23 执行引擎:如何把握 ShardingSphere 中的 Executor 执行模型?(下).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/24 归并引擎:如何理解数据归并的类型以及简单归并策略的实现过程?.md.html">24 归并引擎:如何理解数据归并的类型以及简单归并策略的实现过程?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/25 归并引擎:如何理解流式归并和内存归并在复杂归并场景下的应用方式?.md.html">25 归并引擎:如何理解流式归并和内存归并在复杂归并场景下的应用方式?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/26 读写分离:普通主从架构和分片主从架构分别是如何实现的?.md.html">26 读写分离:普通主从架构和分片主从架构分别是如何实现的?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
<a class="current-tab" href="/专栏/ShardingSphere 核心原理精讲-完/27 分布式事务:如何理解 ShardingSphere 中对分布式事务的抽象过程?.md.html">27 分布式事务:如何理解 ShardingSphere 中对分布式事务的抽象过程?.md.html</a>
|
||
|
||
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/28 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(上).md.html">28 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(上).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/29 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(下).md.html">29 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(下).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/30 数据脱敏:如何基于改写引擎实现低侵入性数据脱敏方案?.md.html">30 数据脱敏:如何基于改写引擎实现低侵入性数据脱敏方案?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/31 配置中心:如何基于配置中心实现配置信息的动态化管理?.md.html">31 配置中心:如何基于配置中心实现配置信息的动态化管理?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/32 注册中心:如何基于注册中心实现数据库访问熔断机制?.md.html">32 注册中心:如何基于注册中心实现数据库访问熔断机制?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/33 链路跟踪:如何基于 Hook 机制以及 OpenTracing 协议实现数据访问链路跟踪?.md.html">33 链路跟踪:如何基于 Hook 机制以及 OpenTracing 协议实现数据访问链路跟踪?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/34 系统集成:如何完成 ShardingSphere 内核与 Spring+SpringBoot 的无缝整合?.md.html">34 系统集成:如何完成 ShardingSphere 内核与 Spring+SpringBoot 的无缝整合?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/35 结语:ShardingSphere 总结及展望.md.html">35 结语:ShardingSphere 总结及展望.md.html</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>27 分布式事务:如何理解 ShardingSphere 中对分布式事务的抽象过程?</h1>
|
||
|
||
<p>从今天开始,我们将进入一个全新模块,即<strong>ShardingSphere 分布式事务</strong>。这是一个非常重要的主题,我们将通过三个课时来全面介绍 ShardingSphere 的事务实现机制。</p>
|
||
|
||
<h3>ShardingTransactionManagerEngine</h3>
|
||
|
||
<p>在第 18 课时和第 19 课时的“分布式事务:如何使用强一致事务与柔性事务?”中,我们已经对分布式事务的基本概念做了介绍。</p>
|
||
|
||
<p>在了解完一些基本概念之后,我们来看 ShardingSphere 中分布式事务模块的代码工程组织结构,我们发现存在三个底层的工程,即 sharding-transaction-core、sharding-transaction-2pc 和 sharding-transaction-base。</p>
|
||
|
||
<p>从命名上,我们不难看出 sharding-transaction-core 应该包含了分布式事务相关的一些基础核心类,而 sharding-transaction-2pc 和 sharding-transaction-base 分别基于强一致性和最终一致性的两种实现。</p>
|
||
|
||
<p>这些包结构的命名实际上可以体现在事务类型 TransactionType 的定义上,它是一个枚举,分别代表了本地事务、XA 二阶段提交事务和 BASE 柔性事务,如下所示:</p>
|
||
|
||
<pre><code>public enum TransactionType {
|
||
|
||
LOCAL, XA, BASE
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>TransactionType 类位于 sharding-transaction-core 工程中,让我们先来看一下这个工程中的其他内容,首先其冲的就是 ShardingTransactionManagerEngine 接口。</p>
|
||
|
||
<p>在前面的课程中,我们在 ShardingRuntimeContext 这个上下文对象中第一次看到这个分布式事务管理器的入口,如下所示:</p>
|
||
|
||
<pre><code>public final class ShardingRuntimeContext extends AbstractRuntimeContext<ShardingRule> {
|
||
|
||
…
|
||
|
||
private final ShardingTransactionManagerEngine shardingTransactionManagerEngine;
|
||
|
||
|
||
|
||
public ShardingRuntimeContext(final Map<String, DataSource> dataSourceMap, final ShardingRule rule, final Properties props, final DatabaseType databaseType) throws SQLException {
|
||
|
||
…
|
||
|
||
//创建分布式事务管理器引擎并初始化
|
||
|
||
shardingTransactionManagerEngine = new ShardingTransactionManagerEngine();
|
||
|
||
shardingTransactionManagerEngine.init(databaseType, dataSourceMap);
|
||
|
||
}
|
||
|
||
…
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>在 ShardingTransactionManagerEngine 的构造函数中,调用了如下所示的 loadShardingTransactionManager 方法:</p>
|
||
|
||
<pre><code>private final Map<TransactionType, ShardingTransactionManager> transactionManagerMap = new EnumMap<>(TransactionType.class);
|
||
|
||
|
||
|
||
private void loadShardingTransactionManager() {
|
||
|
||
//通过 ServiceLoader 加载 ShardingTransactionManager 实现类
|
||
|
||
for (ShardingTransactionManager each : ServiceLoader.load(ShardingTransactionManager.class)) {
|
||
|
||
if (transactionManagerMap.containsKey(each.getTransactionType())) {
|
||
|
||
log.warn("Find more than one {} transaction manager implementation class, use `{}` now",
|
||
|
||
each.getTransactionType(), transactionManagerMap.get(each.getTransactionType()).getClass().getName());
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
transactionManagerMap.put(each.getTransactionType(), each);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>可以看到,这里直接使用了 JDK 中 ServiceLoader 工具类的 load 方法来加载 ShardingTransactionManager 的实现类,这是使用 SPI 实现微内核架构的最直接的方式,上述方法的作用就是加载类路径上的 ShardingTransactionManager 并缓存在内存中。在 ShardingSphere 中,ShardingTransactionManager 是对分布式事务管理器的一种抽象。我们在后续内容中会具体展开。</p>
|
||
|
||
<p>然后,我们看到在 ShardingRuntimeContext 中,当构建完 ShardingTransactionManagerEngine 对象之后,会调用它的 init 方法进行初始化,如下所示:</p>
|
||
|
||
<pre><code>public void init(final DatabaseType databaseType, final Map<String, DataSource> dataSourceMap) {
|
||
|
||
for (Entry<TransactionType, ShardingTransactionManager> entry : transactionManagerMap.entrySet()) {
|
||
|
||
entry.getValue().init(databaseType, getResourceDataSources(dataSourceMap));
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
private Collection<ResourceDataSource> getResourceDataSources(final Map<String, DataSource> dataSourceMap) {
|
||
|
||
List<ResourceDataSource> result = new LinkedList<>();
|
||
|
||
for (Map.Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
|
||
|
||
//创建 ResourceDataSource 对象
|
||
|
||
result.add(new ResourceDataSource(entry.getKey(), entry.getValue()));
|
||
|
||
}
|
||
|
||
return result;
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>这部分的代码相当于是执行所获取 ShardingTransactionManager 的 init 方法对其进行初始化。在初始化过程中,我们在这里还看到了一个新的数据源对象 ResourceDataSource,如下所示:</p>
|
||
|
||
<pre><code>public final class ResourceDataSource {
|
||
|
||
private final String originalName;
|
||
|
||
private String uniqueResourceName;
|
||
|
||
private final DataSource dataSource;
|
||
|
||
|
||
|
||
public ResourceDataSource(final String originalName, final DataSource dataSource) {
|
||
|
||
this.originalName = originalName;
|
||
|
||
this.dataSource = dataSource;
|
||
|
||
this.uniqueResourceName = ResourceIDGenerator.getInstance().nextId() + originalName;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>ResourceDataSource 的作用就是保存数据库名和 DataSource 的信息,并为这个 ResourceDataSource 构建一个唯一的资源名称,构建过程使用了 ResourceIDGenerator 工具类。</p>
|
||
|
||
<p>我们可以学习一下它的实现方法,如下所示,可以看到这里用到了单例模式和原子类 AtomicInteger:</p>
|
||
|
||
<pre><code>public final class ResourceIDGenerator {
|
||
|
||
|
||
|
||
private static final ResourceIDGenerator INSTANCE = new ResourceIDGenerator();
|
||
|
||
private final AtomicInteger count = new AtomicInteger();
|
||
|
||
|
||
|
||
//创建单例
|
||
|
||
public static ResourceIDGenerator getInstance() {
|
||
|
||
return INSTANCE;
|
||
|
||
}
|
||
|
||
|
||
|
||
String nextId() {
|
||
|
||
return String.format("resource-%d-", count.incrementAndGet());
|
||
|
||
}
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>让我们再回到 ShardingTransactionManagerEngine,来看它的 getTransactionManager 方法,如下所示:</p>
|
||
|
||
<pre><code>public ShardingTransactionManager getTransactionManager(final TransactionType transactionType) {
|
||
|
||
ShardingTransactionManager result = transactionManagerMap.get(transactionType);
|
||
|
||
if (TransactionType.LOCAL != transactionType) {
|
||
|
||
Preconditions.checkNotNull(result, "Cannot find transaction manager of [%s]", transactionType);
|
||
|
||
}
|
||
|
||
return result;
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>这里只是根据事务类型获取了对应的 ShardingTransactionManager。最后,ShardingTransactionManagerEngine 中还有一个 close 方法,如下所示:</p>
|
||
|
||
<pre><code>public void close() throws Exception {
|
||
|
||
for (Entry<TransactionType, ShardingTransactionManager> entry : transactionManagerMap.entrySet()) {
|
||
|
||
entry.getValue().close();
|
||
|
||
}
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>通过上述分析,我们可以认为,ShardingTransactionManagerEngine 作为分布式事务的入口,更多的起到对 ShardingTransactionManager 的管理和维护作用,相当于是一个容器。</p>
|
||
|
||
<p>那么 ShardingTransactionManager 是如何运作的呢?让我们一起来看一下。</p>
|
||
|
||
<h3>ShardingTransactionManager</h3>
|
||
|
||
<p>ShardingTransactionManager 接口位于 sharding-transaction-core 工程的 org.apache.shardingsphere.transaction.spi 包中,定义如下:</p>
|
||
|
||
<pre><code>public interface ShardingTransactionManager extends AutoCloseable {
|
||
|
||
|
||
|
||
//根据数据库类型和 ResourceDataSource 进行初始化
|
||
|
||
void init(DatabaseType databaseType, Collection<ResourceDataSource> resourceDataSources);
|
||
|
||
|
||
|
||
//获取 TransactionType
|
||
|
||
TransactionType getTransactionType();
|
||
|
||
|
||
|
||
//判断是否在事务中
|
||
|
||
boolean isInTransaction();
|
||
|
||
|
||
|
||
//获取支持事务的 Connection
|
||
|
||
Connection getConnection(String dataSourceName) throws SQLException;
|
||
|
||
|
||
|
||
//开始事务
|
||
|
||
void begin();
|
||
|
||
|
||
|
||
//提交事务
|
||
|
||
void commit();
|
||
|
||
|
||
|
||
//回滚事务
|
||
|
||
void rollback();
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>我们看到从该接口中可以根据 DataSource 名获取 Connection。同时,对比后面要介绍的 JTA 中的 TransactionManager 接口,我们同样找到了作为一个事务管理器而言所必需的 begin、commit 和 rollback 这三个基本操作。ShardingSphere 还为这些基本操作专门提供了一个枚举 TransactionOperationType。</p>
|
||
|
||
<p>我们通过查看 ShardingSphere 中 ShardingTransactionManager 的类层结构,发现存在两个实现类,即 XAShardingTransactionManager 和 SeataATShardingTransactionManager 类。</p>
|
||
|
||
<h4>1.XAShardingTransactionManager</h4>
|
||
|
||
<p>要理解基于 XA 协议的 ShardingTransactionManager,我们同样需要具备一定的理论知识。XA 是由 X/Open 组织提出的两阶段提交协议,是一种分布式事务的规范,XA 规范主要定义了面向全局的事务管理器 TransactionManager(TM)和面向局部的资源管理器 ResourceManager(RM)之间的接口。</p>
|
||
|
||
<p>XA 接口是双向的系统接口,在 TransactionManager,以及一个或多个 ResourceManager 之间形成通信桥梁。通过这样的设计,TransactionManager 控制着全局事务,管理事务生命周期,并协调资源,而 ResourceManager 负责控制和管理包括数据库相关的各种实际资源。</p>
|
||
|
||
<p>XA 的整体结构以及 TransactionManager 和 ResourceManager 之间的交互过程参考下图:</p>
|
||
|
||
<p><img src="assets/Ciqc1F9fO8mADc8eAAClhM8LyC0111.png" alt="Drawing 0.png" /></p>
|
||
|
||
<p>XA 协议组成结构图</p>
|
||
|
||
<p>所有关于分布式事务的介绍中都必然会讲到两阶段提交,因为它是实现 XA 分布式事务的关键。我们知道在两阶段提交过程中,存在协调者和参与者两种角色。在上图中,XA 引入的 TransactionManager 充当着全局事务中的“协调者”角色,而图中的 ResourceManager 相当于“参与者”角色,对自身内部的资源进行统一管理。</p>
|
||
|
||
<p>理解了这些概念之后,我们再来看 Java 中的实现。作为 Java 平台中的事务规范,JTA(Java Transaction API)也定义了对 XA 事务的支持。实际上,JTA 是基于 XA 架构进行建模的,在 JTA 中,事务管理器抽象为 javax.transaction.TransactionManager 接口,并通过底层事务服务进行实现。</p>
|
||
|
||
<p>和很多其他的 Java 规范一样,JTA 仅仅定义了接口,具体的实现则是由供应商负责提供。目前JTA 的实现分成两大类,其中一类是直接集成在应用服务器中,例如 JBoss;另一类则是独立的实现,例如 ShardingSphere 中所采用的 Atomikos 和 Bitronix,这些实现可以应用在那些不使用 J2EE 应用服务器的环境里(例如普通的 Java 应用)用以提供分布式事务保证。另一方面,JTA 接口里的 ResourceManager 同样需要数据库厂商提供 XA 的驱动实现。</p>
|
||
|
||
<p>接下来,让我们对 JTA 中的相关核心类做进一步分析,这些内容是后续理解 ShardingSphere 中分布式事务实现机制的基础。</p>
|
||
|
||
<p>在 JTA 中,提供了以下几个核心接口:</p>
|
||
|
||
<ul>
|
||
|
||
<li><strong>UserTransaction</strong></li>
|
||
|
||
</ul>
|
||
|
||
<p>该接口是面向开发人员的接口,能够编程控制事务处理。</p>
|
||
|
||
<ul>
|
||
|
||
<li><strong>TransactionManager</strong></li>
|
||
|
||
</ul>
|
||
|
||
<p>通过该接口允许应用程序服务器来控制分布式事务。</p>
|
||
|
||
<ul>
|
||
|
||
<li><strong>Transaction</strong></li>
|
||
|
||
</ul>
|
||
|
||
<p>代表正在管理应用程序的事务。</p>
|
||
|
||
<ul>
|
||
|
||
<li><strong>XAResource</strong></li>
|
||
|
||
</ul>
|
||
|
||
<p>这是一个面向提供商的实现接口,是一个基于 XA 协议的 Java 映射,各个数据库提供商在提供访问自己资源的驱动时,必须实现这样的接口。</p>
|
||
|
||
<p>另外,在 javax.sql 包中还存在几个与 XA 相关的核心类,即代表连接的 XAConnection、代表数据源的 XADataSource,以及代表事务编号的 Xid。</p>
|
||
|
||
<p>我们采用上述核心类来简单模拟一下基于 XA 的分布式事务的常见实现过程的伪代码。对于一个跨库操作而言,一般我们可以基于 UserTransaction 接口实现如下的操作流程:</p>
|
||
|
||
<pre><code>UserTransaction userTransaction = null;
|
||
|
||
Connection connA = null;
|
||
|
||
Connection connB = null;
|
||
|
||
try{
|
||
|
||
userTransaction.begin();
|
||
|
||
//实现跨库操作
|
||
|
||
connA.execute("sql1")
|
||
|
||
connB.execute("sql2")
|
||
|
||
userTransaction.commit();
|
||
|
||
}catch(){
|
||
|
||
userTransaction.rollback();
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>要想上述代码发挥作用,这里的连接对象 Connection 就得支持 XAResource 接口,也就涉及一系列关于 XADataSource 和 XAConnection 的处理过程。</p>
|
||
|
||
<p>让我们回到 ShardingSphere,来看 XAShardingTransactionManager 类,该类是分布式事务的 XA 实现类,它主要负责对实际的 DataSource 进行管理和适配,并且将接入端事务的 begin/commit/rollback 操作委托给具体的 XA 事务管理器,例如 XAShardingTransactionManager 就会使用 XATransactionManager 中的 TransactionManager 完成 commit 操作:</p>
|
||
|
||
<pre><code>@Override
|
||
|
||
public void commit() {
|
||
|
||
xaTransactionManager.getTransactionManager().commit();
|
||
|
||
}
|
||
|
||
</code></pre>
|
||
|
||
<p>这里的 XATransactionManager 就是对各种第三方 XA 事务管理器的一种抽象,封装了对</p>
|
||
|
||
<p>Atomikos、Bitronix 等第三方工具的实现方式。我们会在下一课时中对这个 XATransactionManager 以及 XAShardingTransactionManager 进行具体展开。</p>
|
||
|
||
<p>作为总结,我们梳理在 ShardingSphere 中与 XA 两阶段提交相关的核心类之间的关系,如下图所示:</p>
|
||
|
||
<p><img src="assets/Ciqc1F9fO-GALidCAABl39blOv8975.png" alt="Drawing 2.png" /></p>
|
||
|
||
<h4>2.SeataATShardingTransactionManager</h4>
|
||
|
||
<p>介绍完 XAShardingTransactionManager 之后,我们来看上图中 ShardingTransactionManager 接口的另一个实现类 SeataATShardingTransactionManager。因为基于不同技术体系和工作原理,所以 SeataATShardingTransactionManager 中的实现方法也完全不同,让我们来看一下。</p>
|
||
|
||
<p>在介绍 SeataATShardingTransactionManager 之前,我们同样有必要对 Seata 本身做一些展开。与 XA 不同,<strong>Seata 框架</strong>中一个分布式事务包含三种角色,除了 XA 中同样具备的 TransactionManager(TM)和 ResourceManager(RM) 之外,还存在一个事务协调器 TransactionCoordinator (TC),维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。</p>
|
||
|
||
<p>其中,TM 是一个分布式事务的发起者和终结者,TC 负责维护分布式事务的运行状态,而 RM 则负责本地事务的运行。</p>
|
||
|
||
<p>Seata 的整体架构图如下所示:</p>
|
||
|
||
<p><img src="assets/Ciqc1F9fO-uAZyMCAAEoW9aLAuQ436.png" alt="Drawing 4.png" /></p>
|
||
|
||
<p>Seata 分布式事务组成结构图(来自 Seata 官网)</p>
|
||
|
||
<p>基于Seata 框架,一个分布式事务的执行流程包含如下五个步骤:</p>
|
||
|
||
<p><img src="assets/Ciqc1F9fO_WAHRyqAACL8p3Fa_E119.png" alt="Drawing 5.png" /></p>
|
||
|
||
<p>我们同样会在下一课时中对这些步骤,以及其中涉及的核心类进行具体展开。</p>
|
||
|
||
<h3>从源码解析到日常开发</h3>
|
||
|
||
<p>今天的内容我们主要关注于 ShardingSphere 中对分布式事务的抽象过程,本身没有涉及过多的源码分析。我们学习的关注点在于掌握 XA 协议的特点和核心类,以及基于 Seata 框架完成一次分布式事务执行的过程。</p>
|
||
|
||
<h3>小结与预告</h3>
|
||
|
||
<p>这是介绍 ShardingSphere 分布式事务实现原理的第一个课时,我们重点讲解了 ShardingSphere 是如何基于同一套体系来完成对 XA 和 Seata 这两种不同分布式事务实现方式的统一抽象和封装。</p>
|
||
|
||
<p>这里给你留一道思考题:ShardingSphere 对不同的分布式事务实现技术做了哪些抽象?欢迎你在留言区与大家讨论,我将逐一点评解答。</p>
|
||
|
||
<p>在接下来的两个课时中,我们就将基于今天讲到的很多概念和步骤,从源码角度出发深入剖析分布式事务的实现原理和过程。</p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/26 读写分离:普通主从架构和分片主从架构分别是如何实现的?.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/ShardingSphere 核心原理精讲-完/28 分布式事务:ShardingSphere 中如何集成强一致性事务和柔性事务支持?(上).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":"709974dc1b5b3d60","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>
|
||
|