seata源码分析之-AT模式客户端分析3

本文详细分析了Seata AT模式下,RM(Resource Manager)如何通过DataSourceProxy代理数据源,处理RPC调用并执行SQL。在执行过程中,Seata进行SQL解析,根据操作类型创建不同执行器,并在执行前进行镜像查询,准备回滚日志。执行器在提交时会涉及全局事务的XID,以及处理锁冲突的LockRetryPolicy。整个流程揭示了Seata如何实现分布式事务的自动化管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RM的大致执行流程图

在这里插入图片描述

RM之StorageService

前面我们分析了springcloud的例子中的TMBusinessService的一些原理,接下来我们来找个RM说下,比如说storage微服务,因为他看起来比较简单,就是一个更新操作:
在这里插入图片描述
我们可以看到这个就是一个普通的FeignRPC调用,好像什么都没做,除了GlobalTransactionScanner干的初始化TMRM之外的事,还有什么呢,那怎么就参与到了全局事务中了,想想应该也是做了什么代理对吧。
在这里插入图片描述

DataSourceProxy数据源代理

我们可以在StorageApplication中看到这些,可以想到我们JDBC用的数据源被代理了。
在这里插入图片描述

DataSourceProxy的一些方法

于是我很好奇的看了下这个类,找了一些方法,好像在创建的时候就会去TC那里注册分支事务,然后默认是AT模式:
在这里插入图片描述
内部还是用Netty组件去进行注册,这个具体就不深入了。
在这里插入图片描述
在这里插入图片描述

处理RPC调用

我们看到很常规的调用过来了,然后进行更新。
在这里插入图片描述
一直跟进去到execute
在这里插入图片描述
主要是这里,可以看到这里的连接已经是代理的了:
在这里插入图片描述
里面如果发现是AT模式,就进行SQL的解析,封装成PreparedStatementProxy返回。
在这里插入图片描述
最后调用PreparedStatementProxyexecuteUpdate里面是ExecuteTemplateexecute,其实就是做了代理,里面用了一个ExecuteTemplate
在这里插入图片描述

ExecuteTemplate的execute

首先判断是否需要代理,不需要就直接正常执行了,否则就看数据库类型,找出对应的sql识别器,对一些操作进行识别,根据不同的操作创建对应的执行器,比如这里是更新的话就是UpdateExecutor,然后调用执行方法。

 public static <T, S extends Statement> T execute(List<SQLRecognizer> sqlRecognizers,
                                                     StatementProxy<S> statementProxy,
                                                     StatementCallback<T, S> statementCallback,
                                                     Object... args) throws SQLException {
        if (!RootContext.requireGlobalLock() && BranchType.AT != RootContext.getBranchType()) {
            // Just work as original statement
            return statementCallback.execute(statementProxy.getTargetStatement(), args);
        }

        String dbType = statementProxy.getConnectionProxy().getDbType();
        if (CollectionUtils.isEmpty(sqlRecognizers)) {
            sqlRecognizers = SQLVisitorFactory.get(
                    statementProxy.getTargetSQL(),
                    dbType);
        }
        Executor<T> executor;
        if (CollectionUtils.isEmpty(sqlRecognizers)) {
            executor = new PlainExecutor<>(statementProxy, statementCallback);
        } else {
            if (sqlRecognizers.size() == 1) {
                SQLRecognizer sqlRecognizer = sqlRecognizers.get(0);
                switch (sqlRecognizer.getSQLType()) {
                    case INSERT:
                        executor = EnhancedServiceLoader.load(InsertExecutor.class, dbType,
                                new Class[]{StatementProxy.class, StatementCallback.class, SQLRecognizer.class},
                                new Object[]{statementProxy, statementCallback, sqlRecognizer});
                        break;
                    case UPDATE:
                        executor = new UpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer);
                        break;
                    case DELETE:
                        executor = new DeleteExecutor<>(statementProxy, statementCallback, sqlRecognizer);
                        break;
                    case SELECT_FOR_UPDATE:
                        executor = new SelectForUpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer);
                        break;
                    default:
                        executor = new PlainExecutor<>(statementProxy, statementCallback);
                        break;
                }
            } else {
                executor = new MultiExecutor<>(statementProxy, statementCallback, sqlRecognizers);
            }
        }
        T rs;
        try {
            rs = executor.execute(args);
        } catch (Throwable ex) {
            if (!(ex instanceof SQLException)) {
                // Turn other exception into SQLException
                ex = new SQLException(ex);
            }
            throw (SQLException) ex;
        }
        return rs;
    }

BaseTransactionalExecutor的execute

调用了父类的执行方法,获取全局事务XID绑定,这个XID哪里来的,其实是RPC调用的时候放在头信息里带过来的,有兴趣的可以研究下SeataHandlerInterceptor,最终是doExecute
在这里插入图片描述
SeataHandlerInterceptor进行了XID的获取:
在这里插入图片描述
默认是自动提交的:
在这里插入图片描述
这里ConnectionProxy进行setAutoCommit设置的时候做了一些事情。
在这里插入图片描述
设置false的时候先不提交,最后设置true的时候才提交:
在这里插入图片描述

LockRetryPolicy的execute

这个就是为了处理全局锁冲突重试的,其实可以理解为一个分布式锁。
在这里插入图片描述
可以看到,他会进行重试,然后睡眠一定时间,一定次重试后就会抛出异常。
在这里插入图片描述
在这里插入图片描述

镜像查询与回滚日志插入

然后调用要执行的方法executeAutoCommitFalse
在这里插入图片描述
这里主要是进行前后镜像查询,准备回滚日志信息,为什么要进行前后镜像查询了,其实就是为了回滚的时候可以判断要回滚或者是否有脏写,如果回滚的时候发现现在数据和前镜像一样,其实就不需要回滚,如果发现和后镜像一样,那就要回滚,如果发现不一样,那说明有其他事务可能修改了,那可能要手动处理了
在这里插入图片描述
我们看看他在准备回滚日志的时候做了什么,可以看到生成了一个锁,其实就是进行隔离写的行锁,格式是表名_主键值,将回滚信息放入ConnectionProxy中:
在这里插入图片描述
然后回滚信息里有前后镜像的信息:
在这里插入图片描述
然后进行提交,其实还没有真正提交,还要做一些事情:
在这里插入图片描述

在这里插入图片描述
最终调用ConnectionProxyprocessGlobalTransactionCommit
在这里插入图片描述

好了,篇幅有点长了,后面再讲全局事务提交方法做了什么。
总结下这篇的东西:

  • RM对数据源,连接,处理器进行了代理
  • 会对sql进行解析,根据数据源类型和操作类型生成不同的执行器。
  • 执行器进行提交前会进行前后镜像的查询,然后执行sql语句,创建回滚日志,准备和TC进行提交通信。

今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值