从一个简单的AT模式事务例子入手

本文详细分析了Seata AT模式的全局事务处理,从@GlobalTransactional注解入口,到GlobalTransactionalInterceptor拦截器,再到transactionalTemplate的执行流程。讲解了如何开启和管理全局事务,以及DataSourceProxy数据源代理在事务提交和回滚中的关键作用。通过源码阅读,揭示了Seata在分布式事务中的核心实现机制。
摘要由CSDN通过智能技术生成

傅青阳眯了一下眼睛,心想这个手下飘了,不给点颜色看看是不行了:talk is cheap show me the code。从AT模式的源码来开始讲整个流程。
元始背转身,负手而立,白衣飘飘,一副胸有成竹的酷拽模样。右手一挥:
我先讲AT模式的源码,然后再看TCC模式、XA模式,最后才是Saga模式,因为AT、TCC、XA这3个模式基本上是使用同一套分布式框架基础组件完成的,而Saga是基于状态机引擎完成的,底层原理不一样。


画外音:
阅读源码之前需要先下载Seata的最新源码,我们使用的是v1.4.2版本;然后下载Seata的官方例子。最后为了调试方便,还需要将官方例子中的一些典型例子代码复制到源码中,作为源码的一个模块,这样在调试的时候就能直接进入到Seata源码中进行断点了,而且还能写注释。

比如我们阅读AT模式源码的时候,可以将seata-samples中的典型例子springboot-dubbo-seata拷贝到seata源码中。拷贝例子需要注意将例子的parent设置为seata-parent。

然后本地需要准备的环境有:
JDK1.8、maven3.8.1、IDEA、MySQL安装教程Nacos安装教程、Seata Server可以直接解压执行。
如果遇上源码编译的问题,参考这个文档
最后将springboot-dubbo-seata这个demo运行起来。


元始双手缓缓向上托起,一块34寸透明的曲面显示器凭空浮现,叮咚一声,经典的暗黑色的IDEA界面就开始打开:在开始看AT模式的源码之前,先回顾一下AT模式主要实现的两个任务:

  1. 获取全局锁、开启和提交/回滚全局事务。
  2. 解析SQL并写入undolog。

然后在Demo的代码,我发现跟Seata框架相关的代码有以下几个地方:

  1. 2个配置文件:file.conf、registry.conf。等分析完主干流程后再来分析配置文件,会更加清晰明了。
  2. @GlobalTransactional注解:这个是全局事务的入口。
  3. DataSourceProxy:数据源代理类。
  4. GlobalTransactionScanner:全局事务扫描组件。

那我就从这些地方开始讲起。

GlobalTransactionalInterceptor 全局事务拦截器

这里我们先看看全局事务的入口处理。@GlobalTransactional 这个注解的业务逻辑实现是在GlobalTransactionalInterceptor这个拦截器。看一下主入口代码:

/**
 * 全局事务的入口
 */
@Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
    //......
    if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
        //...
        if (!localDisable) {
            // 全局事务处理
            if (globalTransactionalAnnotation != null) {
                return handleGlobalTransaction(methodInvocation, globalTransactionalAnnotation);
            } else if (globalLockAnnotation != null) {
                return handleGlobalLock(methodInvocation, globalLockAnnotation);
            }
        }
    }
    return methodInvocation.proceed();
}

实际处理在handleGlobalTransaction方法,继续跟进看看:

Object handleGlobalTransaction(final MethodInvocation methodInvocation,
    final GlobalTransactional globalTrxAnno) throws Throwable {
    boolean succeed = true;
    try {
        return transactionalTemplate.execute(new TransactionalExecutor() {// 模板模式应用
            //......
        });
    } //......
}

这里我们先只看主干流程,其他的细节暂时先忽略。这里可以看到就是执行了transactionalTemplate.execute 这个模板方法,我们继续跟进看看。

public Object execute(TransactionalExecutor business) throws Throwable {
    //......
    // 事务的传播属性
    Propagation propagation = txInfo.getPropagation();
    SuspendedResourcesHolder suspendedResourcesHolder = null;
    try {
        switch (propagation) {
            //......
            case REQUIRED:
                break;
            //......
        }
        //......
        try {
            // 开启全局事务
            beginTransaction(txInfo, tx);

            Object rs;
            try {
                // Do Your Business  执行业务逻辑
                rs = business.execute();
            } catch (Throwable ex) {
                // 3. The needed business exception to rollback.
                // 执行全局事务回滚
                completeTransactionAfterThrowing(txInfo, tx, ex);
                throw ex;
            }

            // 4. everything is fine, commit.
            // 执行全局事务提交
            commitTransaction(tx);

            return rs;
        } finally {
            //......
        }
    } //......
}

从上面的代码我们可以很清晰地看到执行的主干流程,就是先开启全局事务,然后在try中执行业务逻辑,如果一切顺序就提交全局事务,如果出现异常就执行全局事务回滚。

傅青阳紧接着问道:那数据源如何管理,事务的回滚肯定需要数据源的配合。
元始:老大放心,数据源是用代理类DataSourceProxy实现的,接下去看。

DataSourceProxy 数据源代理类

DataSourceProxy 这个数据源代理跟进代码,可以发现里面又提供了连接代理ConnectionProxy

/**
 * 数据库连接
 */
@Override
public ConnectionProxy getConnection() throws SQLException {
    Connection targetConnection = targetDataSource.getConnection();
    return new ConnectionProxy(this, targetConnection);
}

从这里我们可以大胆猜测一下,因为事务的提交和回滚都是通过Connection.commitConnection.rollback实现的,因此在连接代理类ConnectionProxy里面肯定会对事务的提交和回滚增加一些Seata框架的处理,比如在本地事务提交之前生成DML语句的前后镜像数据,全局事务提交之后清理本地数据库的undolog数据,本地事务如果回滚则将前镜像数据恢复回来就行了。

说到这里,元始又甩出了一块白板,三两下画了一张图来总结一下:

全局事务入口和数据源代理

傅青阳内心鄙视了一下,花里胡哨的东西真多:你这里的全局事务这边跟数据源代理那边还没串联起来吧。
元始:别急啊,我先喝口可乐润润。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gemini技术窝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值