seata源码分析(AT)-分支事务注册

本文详细剖析了Seata中分支事务的客户端与服务端注册过程,涉及数据源代理、连接获取、SQL预处理、执行逻辑、重试机制、本地事务提交及远程注册。重点讲解了如何通过DataSourceProxy进行事务干预和undolog日志管理。

一、分支事务的注册

分支事务的注册是在seata的一个很重要的数据源DataSourceProxy中去做的,简单来说就是通过数据源这一层去干预了数据库的执行而达到分支事务的注册。

1、客户端的处理

所以我们直接进入DataSourceProxy的源码看获取连接的地方,我就来模拟一个过程就是我们如果在分支事务中要向数据库插入一条数据,那么jdbc的过程如下:

1.获取连接Connection,开始事务;
2.通过连接创建一个预处理对象,比如PreparedStatement = Connection.prepareStatement(sql);
3.通过PreparedStatement 的execute方法得到执行结果;
4.提交本地事务。

带着上面的sql的一个普通事务的执行过程我们来分析下分支事务的执行。

1.1、获取连接

io.seata.rm.datasource.DataSourceProxy#getConnection()

public ConnectionProxy getConnection() throws SQLException {
   
   
    /**
     * 获取连接的时候来调用的数据源连接,这里将数据源连接包了一层
     * 这肯定要包一层,因为因为seata需要注册分支事务ID,注册分支事务需要敢于sql的执行
     * 执行的前后需要插入自己的逻辑,所以这里需要将Connection包装一层
     * 这里包装的是ConnectionProxy
     */
    Connection targetConnection = targetDataSource.getConnection();
    return new ConnectionProxy(this, targetConnection);
}

1.2、sql预处理

sql的预处理肯定是ConnectionProxy中执行的,所以直接定位到ConnectionProxy中
io.seata.rm.datasource.AbstractConnectionProxy#prepareStatement(java.lang.String)

/**
 * sql的预处理,就是执行那个preparing.prepareStatement(sql)就是类似于
 * 我们jdbc的操作的时候的sql预处理,所以这里就是执行业务sql的逻辑入口
 * @param sql
 * @return
 * @throws SQLException
 */
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
   
   
    String dbType = getDbType();
    // support oracle 10.2+
    PreparedStatement targetPreparedStatement = null;
    if (BranchType.AT == RootContext.getBranchType()) {
   
   
        List<SQLRecognizer> sqlRecognizers = SQLVisitorFactory.get(sql, dbType);
        if (sqlRecognizers != null && sqlRecognizers.size() == 1) {
   
   
            SQLRecognizer sqlRecognizer = sqlRecognizers.get(0);
            if (sqlRecognizer != null && sqlRecognizer.getSQLType() == SQLType.INSERT) {
   
   
                TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dbType).getTableMeta(getTargetConnection(),
                        sqlRecognizer.getTableName(), getDataSourceProxy().getResourceId());
                String[] pkNameArray = new String[tableMeta.getPrimaryKeyOnlyName().size()];
                tableMeta.getPrimaryKeyOnlyName().toArray(pkNameArray);
                targetPreparedStatement = getTargetConnection().prepareStatement(sql,pkNameArray);
            }
        }
    }
    if (targetPreparedStatement == null) {
   
   
        //进行sql预处理
        targetPreparedStatement = getTargetConnection().prepareStatement(sql);
    }
    //预处理完成以后返回预处理对象,和jdbc操作sql的预处理一样的,执行完预处理需要返回一个预处理对象
    //我们拿到这个预处理对象需要进行execute得到结果集,所以这里先返回一个预处理对象,只是seata这里
    //给我们返回了一个sql预处理对象的封装而已,因为它要做一些事情,比如分支事务的处理,所以这个对象返回以后
    //下面就可以直接看PreparedStatementProxy的execute方法了
    return new PreparedStatementProxy(this, targetPreparedStatement, sql);
}

1.3、执行execute得到结果

上面的ConnectionProxy得到了PreparedStatementProxy,也就是执行了sql的预处理,这个时候就需要到PreparedStatementProxy里面去看它的execute方法了,这个方法就是得到执行结果,得到执行结果就意味着所有的分支事务逻辑都里面了。
io.seata.rm.datasource.PreparedStatementProxy#execute

/**
 * sql的真正的执行逻辑
 * @return
 * @throws SQLException
 */
@Override
public boolean execute() throws SQLException {
   
   
    //调用ExecuteTemplate模板方法执行得到结果集
    return ExecuteTemplate.execute(this, (statement, args) -> statement.execute());
}


io.seata.rm.datasource.exec.ExecuteTemplate#execute
public static <T, S extends Statement> T execute(List<SQLRecognizer> sqlRecognizers,
                                                 StatementProxy<S> statementProxy,
                                                 StatementCallback<T, S> statementCallback,
                                                 Object... args) throws SQLException {
   
   
    //判断是否是AT模式,如果不是AT模式直接当成普通的sql执行
    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 {
   
   
        //得到sql的分析结果
        if (sqlRecognizers.size() == 1) {
   
   
            SQLRecognizer sqlRecognizer = sqlRecognizers.get(0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值