本文首发于个人微信公众号《andyqian》, 期待你的关注!
前言
这篇文章主要讲述的是分布式事务 seata 框架的 rm - datasource 模块。文章会按照以下几点进行讲解:
- 简介
- 结构
- 源码解析
- 涉及的设计模式
- 后续的扩展
简介
我们知道seata框架本身是事务的协调者,协调多个本地事务“符合事务的特性”,从而构成一个”分布式的全局事务”。这也是seata框架的设计思想。看到这里,我们应该知道,要想将多个本地事务能够组成一个全局”事务”,就应该对本地事务进行改造,让其在同一个“全局事务”内。rm - datasource 模块就是一个这样的角色,其承担的职责包括但不限于以下几点:
- SQL 解析与执行。
- 全局”事务”的注册,提交与回滚。
- undo 日志的生成与使用。
结构
rm-datasource 类图如下所示:
别看有这么多类以及package。别慌,这里有很多我们的老朋友,我们一起来认识一下:
代理类:
StatementProxy,
PreparedStatementProxy,
DataSoureProxy,
ConnectionPrxoy。
这些类分别是我们熟悉的:Statement, PreparedStatement, DataSoure, Connection 的代理类。(下面会以 ConnectionPrxoy 为例,查看其源码。)
exec 模块
该模块承担的职责是:对SQL按照不同的SQL类型进行执行,并保存beforeImage以及afterImage镜像文件到 undo 数据结构中。(用于事务回滚)。
其主要类图如下所示:
- 顶层父类是 Executor 接口,其定义了 executor 的方法。代码也非常简单,如下所示:
public interface Executor<T> {
/**
* Execute t.
*
* @param args the args
* @return the t
* @throws Throwable the throwable
*/
T execute(Object... args) throws Throwable;
}
- BaseTransactionalExecutor 为抽象类,实现了Executor 接口中的 execute 方法。并定义了doExecute() 抽象方法。
execute 方法实现如下:
@Override
public Object execute(Object... args) throws Throwable {
//1. 如果为Global 事务,则将XId 绑定在上下文中
if (RootContext.inGlobalTransaction()) {
String xid = RootContext.getXID();
statementProxy.getConnectionProxy().bind(xid);
}
//2. 是否需要获取全局锁,进行不同设置
if (