Seata AT模式源码解析(二) ------ DataSourceProxy

  由于seata需要兼容现有常用的ORM框架,所以直接使用DataSourceProxy实现DataSource接口,在项目中作为数据源对象。

@Primary
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
	//持有了目标数据源对象,该DataSourceProxy构造函数,会把RootContext的DEFAULT_BRANCH_TYPE设置为BranchType.AT
    return new DataSourceProxy(dataSource);
}

  先看到dataSourceProxy的重写的getConnection方法

@Override
public ConnectionProxy getConnection() throws SQLException {
    //获取targetConnection(目标数据源对象)
    Connection targetConnection = targetDataSource.getConnection();
    //返回的是ConnectionProxy对象,持有了targetConnection
    return new ConnectionProxy(this, targetConnection);
}

  再看到ConnectionProxy实现了Connection接口重写的prepareStatement方法

public PreparedStatement prepareStatement(String sql) throws SQLException {
        String dbType = getDbType();
        // support oracle 10.2+
        PreparedStatement targetPreparedStatement = null;
        //AT模式
        if (BranchType.AT == RootContext.getBranchType()) {
        	//封裝sql的为SQLRecognizer对象
            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()];
                    //获取的到主键的列名填充到pkNameArray中
                    tableMeta.getPrimaryKeyOnlyName().toArray(pkNameArray);
                    //使用targetConnection调用prepareStatement
                    targetPreparedStatement = getTargetConnection().prepareStatement(sql,pkNameArray);
                }
            }
        }
        //不是插入的情况
        if (targetPreparedStatement == null) {
            targetPreparedStatement = getTargetConnection().prepareStatement(sql);
        }
        //返回了PreparedStatementProxy,持有了targetPreparedStatement
        return new PreparedStatementProxy(this, targetPreparedStatement, sql);
}

  再看到PreparedStatementProxy实现了PreparedStatement接口重写的execute方法

public boolean execute() throws SQLException {
		//(statement, args) -> statement.execute(),传入了目标方法的调用
        return ExecuteTemplate.execute(this, (statement, args) -> statement.execute());
}

public static <T, S extends Statement> T execute(List<SQLRecognizer> sqlRecognizers,
                                                     StatementProxy<S> statementProxy,
                                                     StatementCallback<T, S> statementCallback,
                                                     Object... args) throws SQLException {
        //如果RootContext的CONTEXT_HOLDER(threadlocal)的TX_LOCK为空(@GlobalLock注解)且
        //如果没有XID,或者有XID但不是AT模式,直接调用目标方法
        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);
                switch (sqlRecognizer.getSQLType()) {
                	//不同类型的sql交给不同的executor
                    case INSERT:
                    	//spi,加载META-INF/services和META-INF/seata/下的文件(已经加载过的不会重复加载)
                    	//这里获取InsertExecutor的实现类,并使用dbType匹配LoadLevel注解中的name值,取到对应的实现类(mysql对应MySQLInsertExecutor)
                        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;
                }
            } 
            //批量sql
            else {
                executor = new MultiExecutor<>(statementProxy, statementCallback, sqlRecognizers);
            }
        }
        T rs;
        try {
        	//看到execute方法
            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
public T execute(Object... args) throws Throwable {
		//获取XID
        String xid = RootContext.getXID();
   		//有XID的情况下,设置到ConnectionProxy的context(xid)属性中
        if (xid != null) {
            statementProxy.getConnectionProxy().bind(xid);
        }
		
		//判断CONTEXT_HOLDER(threadlocal)的TX_LOCK是否有值
		//如果有值则设置isGlobalLockRequire为true
        statementProxy.getConnectionProxy().setGlobalLockRequire(RootContext.requireGlobalLock());
        //接着看
        return doExecute(args);
}
//AbstractDMLBaseExecutor
public T doExecute(Object... args) throws Throwable {
        AbstractConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
        //判断是否为自动提交,connect的自动提交属性一般为true
        if (connectionProxy.getAutoCommit()) {
            return executeAutoCommitTrue(args);
        } else {
            return executeAutoCommitFalse(args);
        }
}
protected T executeAutoCommitTrue(Object[] args) throws Throwable {
        ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
        try {
        	//首先关闭自动提交
            connectionProxy.setAutoCommit(false);
       		//着重看这里 
            return new LockRetryPolicy(connectionProxy).execute(() -> {
                T result = executeAutoCommitFalse(args);
                connectionProxy.commit();
                return result;
            });
        } catch (Exception e) {
            // when exception occur in finally,this exception will lost, so just print it here
            LOGGER.error("execute executeAutoCommitTrue error:{}", e.getMessage(), e);
            if (!LockRetryPolicy.isLockRetryPolicyBranchRollbackOnConflict()) {
                connectionProxy.getTargetConnection().rollback();
            }
            throw e;
        } finally {
            connectionProxy.getContext().reset();
            connectionProxy.setAutoCommit(true);
        }
    }

  先看到LockRetryPolicy的execute方法,

public <T> T execute(Callable<T> callable) throws Exception {
			//该属性默认为ture,可以通过client.rm.lock.retryPolicyBranchRollbackOnConflict配置
            if (LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT) {
            	//接着看
                return doRetryOnLockConflict(callable);
            } else {
            	//传入的方法直接调用
                return callable.call();
            }
}
protected <T> T doRetryOnLockConflict(Callable<T> callable) throws Exception {
            LockRetryController lockRetryController = new LockRetryController();
            //死循环调用
            while (true) {
                try {
                	//传入的方法直接调用
                    return callable.call();
                } catch (LockConflictException lockConflict) {
                    onException(lockConflict);
                    //如果捕获的是LockConflictException异常(全局锁竞争冲突),则休眠重试,重新竞争全局锁
                    lockRetryController.sleep(lockConflict);
                } catch (Exception e) {
                    onException(e);
                    throw e;	
                }
            }
}
public void sleep(Exception e) throws LockWaitTimeoutException {
		//超过lockRetryTimes重试次数,直接抛出异常了,之后回滚。
        if (--lockRetryTimes < 0) {
            throw new LockWaitTimeoutException("Global lock wait timeout", e);
        }
		
		//休眠lockRetryInternal后重新被while(true)调用callable 
        try {
            Thread.sleep(lockRetryInternal);
        } catch (InterruptedException ignore) {
        }
}
protected void onException(Exception e) throws Exception {
            ConnectionContext context = connection.getContext();
            //UndoItems can't use the Set collection class to prevent ABA
            context.getUndoItems().clear();
            context.getLockKeysBuffer().clear();
            //发生异常回滚
            connection.getTargetConnection().rollback();
}

  再看到executeAutoCommitFalse方法

protected T executeAutoCommitFalse(Object[] args) throws Exception {
        if (!JdbcConstants.MYSQL.equalsIgnoreCase(getDbType()) && isMultiPk()) {
            throw new NotSupportYetException("multi pk only support mysql!");
        }
        //获取sql执行之前的对应的表数据的内容
        TableRecords beforeImage = beforeImage();
        //sql执行
        T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
        //获取sql执行之后的对应的表数据的内容
        TableRecords afterImage = afterImage(beforeImage);
        //构建undo_log
        prepareUndoLog(beforeImage, afterImage);
        return result;
}

  以UpdateExecutor为例,看到beforeImage方法,

protected TableRecords beforeImage() throws SQLException {
        ArrayList<List<Object>> paramAppenderList = new ArrayList<>();
        //获取表的元数据信息,
        TableMeta tmeta = getTableMeta();
        //构建查询sql,查询业务操作前的数据和主键
        String selectSQL = buildBeforeImageSQL(tmeta, paramAppenderList);
        //将查询参数,设置到sql中
        return buildTableRecords(tmeta, selectSQL, paramAppenderList);
}

private String buildBeforeImageSQL(TableMeta tableMeta, ArrayList<List<Object>> paramAppenderList) {
		//大段拼接sql的过程
		
        SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer) sqlRecognizer;
        List<String> updateColumns = recognizer.getUpdateColumns();
        StringBuilder prefix = new StringBuilder("SELECT ");
        StringBuilder suffix = new StringBuilder(" FROM ").append(getFromTableInSQL());
        String whereCondition = buildWhereCondition(recognizer, paramAppenderList);
        if (StringUtils.isNotBlank(whereCondition)) {
            suffix.append(WHERE).append(whereCondition);
        }
        String orderBy = recognizer.getOrderBy();
        if (StringUtils.isNotBlank(orderBy)) {
            suffix.append(orderBy);
        }
        ParametersHolder parametersHolder = statementProxy instanceof ParametersHolder ? (ParametersHolder)statementProxy : null;
        String limit = recognizer.getLimit(parametersHolder, paramAppenderList);
        if (StringUtils.isNotBlank(limit)) {
            suffix.append(limit);
        }
        //会加上FOR UPDATE,锁住数据
        suffix.append(" FOR UPDATE");
        StringJoiner selectSQLJoin = new StringJoiner(", ", prefix.toString(), suffix.toString());
        //ONLY_CARE_UPDATE_COLUMNS默认为true,只会查询被更新的字段
        if (ONLY_CARE_UPDATE_COLUMNS) {
        	//需要把主键一起查询出来
            if (!containsPK(updateColumns)) {
                selectSQLJoin.add(getColumnNamesInSQL(tableMeta.getEscapePkNameList(getDbType())));
            }
            for (String columnName : updateColumns) {
                selectSQLJoin.add(columnName);
            }
        } else {
            for (String columnName : tableMeta.getAllColumns().keySet()) {
                selectSQLJoin.add(ColumnUtils.addEscape(columnName, getDbType()));
            }
        }
        //拼接好的sql返回回去
        return selectSQLJoin.toString();
}
protected TableRecords buildTableRecords(TableMeta tableMeta, String selectSQL, ArrayList<List<Object>> paramAppenderList) throws SQLException {
        ResultSet rs = null;
        //为查询sql构建新的PreparedStatement 
        try (PreparedStatement ps = statementProxy.getConnection().prepareStatement(selectSQL)) {
            if (CollectionUtils.isNotEmpty(paramAppenderList)) {
                for (int i = 0, ts = paramAppenderList.size(); i < ts; i++) {
                    List<Object> paramAppender = paramAppenderList.get(i);
                    for (int j = 0, ds = paramAppender.size(); j < ds; j++) {
                    	//将查询参数设置到sql中
                        ps.setObject(i * ds + j + 1, paramAppender.get(j));
                    }
                }
            }
           	//查询出数据
            rs = ps.executeQuery();
            //数据封装为TableRecords
            return TableRecords.buildRecords(tableMeta, rs);
        } finally {
            IOUtil.close(rs);
        }
}

  再看到afterImage方法

protected TableRecords afterImage(TableRecords beforeImage) throws SQLException {
        TableMeta tmeta = getTableMeta();
        if (beforeImage == null || beforeImage.size() == 0) {
            return TableRecords.empty(getTableMeta());
        }
        //构建查询sql,查询业务操作后的对应的表的数据
        String selectSQL = buildAfterImageSQL(tmeta, beforeImage);
        ResultSet rs = null;
        //设置查询参数,直接从beforeImage获取到主键值,设置进来
        try (PreparedStatement pst = statementProxy.getConnection().prepareStatement(selectSQL)) {
            SqlGenerateUtils.setParamForPk(beforeImage.pkRows(), getTableMeta().getPrimaryKeyOnlyName(), pst);
            rs = pst.executeQuery();
            return TableRecords.buildRecords(tmeta, rs);
        } finally {
            IOUtil.close(rs);
        }
}
private String buildAfterImageSQL(TableMeta tableMeta, TableRecords beforeImage) throws SQLException {
        StringBuilder prefix = new StringBuilder("SELECT ");
        //此时的where条件,只需要主键作为条件,可以从beforeImage拿到主键
        String whereSql = SqlGenerateUtils.buildWhereConditionByPKs(tableMeta.getPrimaryKeyOnlyName(), beforeImage.pkRows().size(), getDbType());
        String suffix = " FROM " + getFromTableInSQL() + " WHERE " + whereSql;
        StringJoiner selectSQLJoiner = new StringJoiner(", ", prefix.toString(), suffix);
        //是否只查询更新的列
        if (ONLY_CARE_UPDATE_COLUMNS) {
            SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer) sqlRecognizer;
            List<String> updateColumns = recognizer.getUpdateColumns();
            //一样要查询出主键
            if (!containsPK(updateColumns)) {
                selectSQLJoiner.add(getColumnNamesInSQL(tableMeta.getEscapePkNameList(getDbType())));
            }
            for (String columnName : updateColumns) {
                selectSQLJoiner.add(columnName);
            }
        } else {
            for (String columnName : tableMeta.getAllColumns().keySet()) {
                selectSQLJoiner.add(ColumnUtils.addEscape(columnName, getDbType()));
            }
        }
        return selectSQLJoiner.toString();
}

  再看到prepareUndoLog方法

protected void prepareUndoLog(TableRecords beforeImage, TableRecords afterImage) throws SQLException {
        if (beforeImage.getRows().isEmpty() && afterImage.getRows().isEmpty()) {
            return;
        }
        //如果是更新,则更新前后的数据条数需要是一样的
        if (SQLType.UPDATE == sqlRecognizer.getSQLType()) {
            if (beforeImage.getRows().size() != afterImage.getRows().size()) {
                throw new ShouldNeverHappenException("Before image size is not equaled to after image size, probably because you updated the primary keys.");
            }
        }
        ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
		
		//如果是删除,则为beforeImage , 删除时的afterImage为空
        TableRecords lockKeyRecords = sqlRecognizer.getSQLType() == SQLType.DELETE ? beforeImage : afterImage;
        //将表名和主键值拼接成一个字符串,后续传递给服务端,控制全局锁
        String lockKeys = buildLockKey(lockKeyRecords);
        connectionProxy.appendLockKey(lockKeys);
		
		//构建undolog对象
        SQLUndoLog sqlUndoLog = buildUndoItem(beforeImage, afterImage);
        connectionProxy.appendUndoLog(sqlUndoLog);
}
protected SQLUndoLog buildUndoItem(TableRecords beforeImage, TableRecords afterImage) {
		//存放了beforeImage,afterImage,tableName,sqlType
			
        SQLType sqlType = sqlRecognizer.getSQLType();
        String tableName = sqlRecognizer.getTableName();

        SQLUndoLog sqlUndoLog = new SQLUndoLog();
        sqlUndoLog.setSqlType(sqlType);
        sqlUndoLog.setTableName(tableName);
        sqlUndoLog.setBeforeImage(beforeImage);
        sqlUndoLog.setAfterImage(afterImage);
        return sqlUndoLog;
}

  executeAutoCommitFalse方法执行完毕后,看到connectionProxy.commit方法。

public void commit() throws SQLException {
        try {
            LOCK_RETRY_POLICY.execute(() -> {
                doCommit();
                return null;
            });
        } catch (SQLException e) {
        	//commit操作发生异常,回滚
            if (targetConnection != null && !getAutoCommit()) {
                rollback();
            }
            throw e;
        } catch (Exception e) {
            throw new SQLException(e);
        }
}
public <T> T execute(Callable<T> callable) throws Exception {
			//该配置为true,则直接调用doCommit()
            if (LOCK_RETRY_POLICY_BRANCH_ROLLBACK_ON_CONFLICT) {
                return callable.call();
            } else {
                return doRetryOnLockConflict(callable);
            }
}
private void doCommit() throws SQLException {
		//判断xid是否为空
        if (context.inGlobalTransaction()) {
            processGlobalTransactionCommit();
        } 
        //判断isGlobalLockRequire是否为true
        else if (context.isGlobalLockRequire()) {
            processLocalCommitWithGlobalLocks();
        } 
        else {
            targetConnection.commit();
        }
}

  先看到processGlobalTransactionCommit方法

 private void processGlobalTransactionCommit() throws SQLException {
        try {
        	//看这里,和seata服务端通信,获取全局锁
            register();
        } catch (TransactionException e) {
        	//包装异常接着抛出,可能抛出LockConflictException (全局锁冲突异常)
            recognizeLockKeyConflictException(e, context.buildLockKeys());
        }
        try {
        	//flushUndoLogs插入本地undo_log表
            UndoLogManagerFactory.getUndoLogManager(this.getDbType()).flushUndoLogs(this);
            //目标连接对象,提交事务,本地锁(for update)被放开,业务sql被提交,undo_log被提交
            targetConnection.commit();
        } catch (Throwable ex) {
            LOGGER.error("process connectionProxy commit error: {}", ex.getMessage(), ex);
            //向seata服务端报告分支事务执行失败。
            report(false);
            throw new SQLException(ex);
        }
        if (IS_REPORT_SUCCESS_ENABLE) {
            //向seata服务端报告分支事务执行成功。
            report(true);
        }
        //清空不再需要的属性值
        context.reset();
}
private void register() throws TransactionException {
        if (!context.hasUndoLog() || context.getLockKeysBuffer().isEmpty()) {
            return;
        }
        //和seata服务端(TC)通信,获得全局锁,并且接收服务端返回的branchId (分支事务id)
        //TC 数据库持久化模式下
        //此时lock_table就会记录,xid  , branchid , 操作的表名和主键值,作为全局锁标志。
        //此时branch_table就会记录,xid  , branchid , 控制和记录分支事务
        Long branchId = DefaultResourceManager.get().branchRegister(BranchType.AT, getDataSourceProxy().getResourceId(),
            null, context.getXid(), null, context.buildLockKeys());
        context.setBranchId(branchId);
}
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException {
        try {
        	//构建分支事务注册请求
            BranchRegisterRequest request = new BranchRegisterRequest();
            request.setXid(xid);
            request.setLockKey(lockKeys);
            request.setResourceId(resourceId);
            request.setBranchType(branchType);
            request.setApplicationData(applicationData);
			
			//netty通信将消息发送给TC
            BranchRegisterResponse response = (BranchRegisterResponse) RmNettyRemotingClient.getInstance().sendSyncRequest(request);
            //分支事务注册失败
            if (response.getResultCode() == ResultCode.Failed) {
            	//抛出异常RmTransactionException,继承自TransactionException
            	//接收到服务端相应的Code
                throw new RmTransactionException(response.getTransactionExceptionCode(), String.format("Response[ %s ]", response.getMsg()));
            }
            return response.getBranchId();
        } catch (TimeoutException toe) {
            throw new RmTransactionException(TransactionExceptionCode.IO, "RPC Timeout", toe);
        } catch (RuntimeException rex) {
            throw new RmTransactionException(TransactionExceptionCode.BranchRegisterFailed, "Runtime", rex);
        }
}
private void recognizeLockKeyConflictException(TransactionException te, String lockKeys) throws SQLException {
		//如果服务端返回的code为LockKeyConflict,则抛出LockConflictException,使得本次事务可以休眠重试
        if (te.getCode() == TransactionExceptionCode.LockKeyConflict) {
            StringBuilder reasonBuilder = new StringBuilder("get global lock fail, xid:" + context.getXid());
            if (StringUtils.isNotBlank(lockKeys)) {
                reasonBuilder.append(", lockKeys:" + lockKeys);
            }
            throw new LockConflictException(reasonBuilder.toString());
        } else {
            throw new SQLException(te);
        }

}

  看到flushUndoLogs方法,插入undo_log表

public void flushUndoLogs(ConnectionProxy cp) throws SQLException {
        ConnectionContext connectionContext = cp.getContext();
        if (!connectionContext.hasUndoLog()) {
            return;
        }
		
        String xid = connectionContext.getXid();
        long branchId = connectionContext.getBranchId();

        BranchUndoLog branchUndoLog = new BranchUndoLog();
        branchUndoLog.setXid(xid);
        branchUndoLog.setBranchId(branchId);
        branchUndoLog.setSqlUndoLogs(connectionContext.getUndoItems());
		
        UndoLogParser parser = UndoLogParserFactory.getInstance();
        byte[] undoLogContent = parser.encode(branchUndoLog);

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Flushing UNDO LOG: {}", new String(undoLogContent, Constants.DEFAULT_CHARSET));
        }
		
		//插入undo_log表
        insertUndoLogWithNormal(xid, branchId, buildContext(parser.getName()), undoLogContent,
            cp.getTargetConnection());
}
protected void insertUndoLogWithNormal(String xid, long branchId, String rollbackCtx,
                                           byte[] undoLogContent, Connection conn) throws SQLException {
        insertUndoLog(xid, branchId, rollbackCtx, undoLogContent, State.Normal, conn);
}
private void insertUndoLog(String xid, long branchId, String rollbackCtx,
                               byte[] undoLogContent, State state, Connection conn) throws SQLException {
        //插入undo_log表
        try (PreparedStatement pst = conn.prepareStatement(INSERT_UNDO_LOG_SQL)) {
            pst.setLong(1, branchId);
            pst.setString(2, xid);
            pst.setString(3, rollbackCtx);
            pst.setBlob(4, BlobUtils.bytes2Blob(undoLogContent));
            pst.setInt(5, state.getValue());
            pst.executeUpdate();
        } catch (Exception e) {
            if (!(e instanceof SQLException)) {
                e = new SQLException(e);
            }
            throw (SQLException) e;
        }
}

   看到report方法,向服务端告知分支事务是否提交成功

private void report(boolean commitDone) throws SQLException {
        if (context.getBranchId() == null) {
            return;
        }
       	//默认5次 可以通过配置client.rm.reportRetryCount修改
        int retry = REPORT_RETRY_COUNT;
        while (retry > 0) {
            try {
            	//commitDone为true,则报告PhaseOne_Done 分支事务一阶段完成
            	//否则报告PhaseOne_Failed 分支事务一阶段失败
                DefaultResourceManager.get().branchReport(BranchType.AT, context.getXid(), context.getBranchId(),
                    commitDone ? BranchStatus.PhaseOne_Done : BranchStatus.PhaseOne_Failed, null);
                return;
            } catch (Throwable ex) {
                LOGGER.error("Failed to report [" + context.getBranchId() + "/" + context.getXid() + "] commit done ["
                    + commitDone + "] Retry Countdown: " + retry);
                retry--;

                if (retry == 0) {
                    throw new SQLException("Failed to report branch status " + commitDone, ex);
                }
            }
        }
}

   processGlobalTransactionCommit看完了,再来看到processLocalCommitWithGlobalLocks的逻辑

private void processLocalCommitWithGlobalLocks() throws SQLException {
        checkLock(context.buildLockKeys());
        try {
        	//直接提交
            targetConnection.commit();
        } catch (Throwable ex) {
            throw new SQLException(ex);
        }
        context.reset();
}
public void checkLock(String lockKeys) throws SQLException {
        if (StringUtils.isBlank(lockKeys)) {
            return;
        }
        //携带lockKeys和TC通信,看是否有全局锁占用这些数据
        // Just check lock without requiring lock by now.
        try {
            boolean lockable = DefaultResourceManager.get().lockQuery(BranchType.AT,
                getDataSourceProxy().getResourceId(), context.getXid(), lockKeys);
            if (!lockable) {	
            	//抛出LockConflictException异常,则可以休眠重试本次事务
                throw new LockConflictException();
            }
        } catch (TransactionException e) {
            //包装异常接着抛出,可能抛出LockConflictException (全局锁冲突异常)
            recognizeLockKeyConflictException(e, lockKeys);
        }
}
public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys)
        throws TransactionException {
        try {
            GlobalLockQueryRequest request = new GlobalLockQueryRequest();
            request.setXid(xid);
            request.setLockKey(lockKeys);
            request.setResourceId(resourceId);

            GlobalLockQueryResponse response = null;
            //发送加全局锁的请求给服务端
            if (RootContext.inGlobalTransaction() || RootContext.requireGlobalLock()) {
                response = (GlobalLockQueryResponse) RmNettyRemotingClient.getInstance().sendSyncRequest(request);
            } else {
                throw new RuntimeException("unknow situation!");
            }
			
			//tc端返回失败状态,抛出TransactionException异常
            if (response.getResultCode() == ResultCode.Failed) {
                throw new TransactionException(response.getTransactionExceptionCode(),
                    "Response[" + response.getMsg() + "]");
            }
            //tc告知是否加上了全局锁
            return response.isLockable();
        } catch (TimeoutException toe) {
            throw new RmTransactionException(TransactionExceptionCode.IO, "RPC Timeout", toe);
        } catch (RuntimeException rex) {
            throw new RmTransactionException(TransactionExceptionCode.LockableCheckFailed, "Runtime", rex);
        }

    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值