Seata源碼分析之Resource

目錄

一、Resource

二、TCCResource

三、DataSourceProxy

四、ConnectionProxy

五、ConnectionContext

六、StatementProxy

七、PreparedStatementProxy

八、ExecuteTemplate


一、Resource

Resource能被ResourceManager管理並且能夠關聯globalTransaction。

public interface Resource {

    // 主從的datasource應該有相同的groupId
    String getResourceGroupId();

    // 當前datasource或者tccResourceId的resourceId
    String getResourceId();

    // 獲取resource的類型, 有AT、TCC兩種
    BranchType getBranchType();
}

二、TCCResource

TCCResource實現Resource接口,爲用戶提供複雜業務sql自定義手動模式

public class TCCResource implements Resource {

    private String resourceGroupId = "DEFAULT";
    private String appName;
    private String actionName;
    private Object targetBean;
    private Method prepareMethod;
    private String commitMethodName;
    private Method commitMethod;
    private String rollbackMethodName;
    private Method rollbackMethod;

    @Override
    public String getResourceGroupId() {
        return resourceGroupId;
    }

    @Override
    public String getResourceId() {
        return actionName;
    }

    @Override
    public BranchType getBranchType() {
        return BranchType.TCC;
    }
}

三、DataSourceProxy

DataSourceProxy實現Resource接口,BranchType爲AT自動模式。它繼承AbstractDataSourceProxy代理類,所有的DataSource相關的方法調用傳入的targetDataSource代理類的方法,除了創建connection方法爲創建ConnectionProxy代理類。對象初始化時獲取連接的jdbcUrl作爲resourceId,並註冊至DefaultResourceManager進行管理。同時還提供獲取原始連接不被代理的getPlainConnection方法。

public class DataSourceProxy extends AbstractDataSourceProxy implements Resource {

    private String resourceGroupId;

    private static final String DEFAULT_RESOURCE_GROUP_ID = "DEFAULT";

    private String jdbcUrl;

    private String dbType;

    public DataSourceProxy(DataSource targetDataSource, String resourceGroupId) {
        super(targetDataSource);
        init(targetDataSource, resourceGroupId);
    }

    private void init(DataSource dataSource, String resourceGroupId) {
        this.resourceGroupId = resourceGroupId;
        try (Connection connection = dataSource.getConnection()) {
            jdbcUrl = connection.getMetaData().getURL();
            dbType = JdbcUtils.getDbType(jdbcUrl, null);
        } catch (SQLException e) {
            throw new IllegalStateException("can not init dataSource", e);
        }
        DefaultResourceManager.get().registerResource(this);
    }

    public Connection getPlainConnection() throws SQLException {
        return targetDataSource.getConnection();
    }

    public String getDbType() {
        return dbType;
    }

    @Override
    public ConnectionProxy getConnection() throws SQLException {
        Connection targetConnection = targetDataSource.getConnection();
        return new ConnectionProxy(this, targetConnection);
    }

    @Override
    public ConnectionProxy getConnection(String username, String password) throws SQLException {
        Connection targetConnection = targetDataSource.getConnection(username, password);
        return new ConnectionProxy(this, targetConnection);
    }

    @Override
    public String getResourceGroupId() {
        return resourceGroupId;
    }

    @Override
    public String getResourceId() {
        if (jdbcUrl.contains("?")) {
            return jdbcUrl.substring(0, jdbcUrl.indexOf("?"));
        } else {
            return jdbcUrl;
        }
    }

    @Override
    public BranchType getBranchType() {
        return BranchType.AT;
    }
}

四、ConnectionProxy

ConnectionProxy繼承AbstractConnectionProxy代理類,創建Statement對象方法爲創建StatementProxy類,創建PreparedStatement對象方法爲創建PreparedStatementProxy代理類。持有ConnectionContext對象,保存當前connection的相關context數據。主要分析它的commit,rollback和setAutoCommit方法。

public class ConnectionProxy extends AbstractConnectionProxy {

    private ConnectionContext context = new ConnectionContext();
    private static final int DEFAULT_REPORT_RETRY_COUNT = 5;
  
    public ConnectionProxy(DataSourceProxy dataSourceProxy, Connection targetConnection) {
        super(dataSourceProxy, targetConnection);
    }

    public ConnectionContext getContext() {
        return context;
    }

    public void bind(String xid) {
        context.bind(xid);
    }

    public void setGlobalLockRequire(boolean isLock) {
        context.setGlobalLockRequire(isLock);
    }

    public boolean isGlobalLockRequire() {
        return context.isGlobalLockRequire();
    }

    // 檢查lockKeys是否鎖住query
    public void checkLock(String lockKeys) throws SQLException {
        // Just check lock without requiring lock by now.
        try {
            boolean lockable = DefaultResourceManager.get().lockQuery(BranchType.AT, getDataSourceProxy().getResourceId(), context.getXid(), lockKeys);
            if (!lockable) {
                throw new LockConflictException();
            }
        } catch (TransactionException e) {
            recognizeLockKeyConflictException(e);
        }
    }

    public void appendUndoLog(SQLUndoLog sqlUndoLog) {
        context.appendUndoItem(sqlUndoLog);
    }

    public void appendLockKey(String lockKey) {
        context.appendLockKey(lockKey);
    }

    // connection事務提交
    public void commit() throws SQLException {
        // 如果開啓了全球事務
        if (context.inGlobalTransaction()) {
	    // 處理全球事務提交邏輯
            processGlobalTransactionCommit();
        } else if (context.isGlobalLockRequire()) {
	    // 如果設置了全球鎖,處理全球鎖提交邏輯
            processLocalCommitWithGlobalLocks();
        } else {
	    // 默認代理連接直接提交
            targetConnection.commit();
        }
    }

    // 處理全球鎖提交邏輯
    private void processLocalCommitWithGlobalLocks() throws SQLException {
       // 檢查鎖先
        checkLock(context.buildLockKeys());
        try {
            targetConnection.commit();
        } catch (Throwable ex) {
            throw new SQLException(ex);
        }
        context.reset();
    }

    // 處理全球事務提交邏輯
    private void processGlobalTransactionCommit() throws SQLException {
        try {
	     // 註冊
            register();
        } catch (TransactionException e) {
            recognizeLockKeyConflictException(e);
        }

        try {
            if (context.hasUndoLog()) {
	        // 如果有UndoLogs,則調用UndoLogManager異步刪除UndoLogs
                UndoLogManager.flushUndoLogs(this);
            }
	    // 代理連接提交
            targetConnection.commit();
        } catch (Throwable ex) {
	    // 提交報錯,報告第一步提交失敗錯誤,讓其他分支數據回滾
            report(false);
            if (ex instanceof SQLException) {
                throw new SQLException(ex);
            }
        }
	// 報告成功
        report(true);
        context.reset();
    }

    // 分支註冊,獲取branchId
    private void register() throws TransactionException {
        Long branchId = DefaultResourceManager.get().branchRegister(BranchType.AT, getDataSourceProxy().getResourceId(),
                null, context.getXid(), null, context.buildLockKeys());
        context.setBranchId(branchId);
    }

    // connection事務回滾調用
    public void rollback() throws SQLException {
        // 數據直接回滾
        targetConnection.rollback();
	// 如果開啓了全球事務,且註冊了當前分支至當前全球事務管理中
	// 提交報錯,報告第一步提交失敗錯誤,讓其他分支數據回滾
        if (context.inGlobalTransaction()) {
            if (context.isBranchRegistered()) {
                report(false);
            }
        }
        context.reset();
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        // change autocommit from false to true, we should commit() first according to JDBC spec.
        if ((autoCommit) && !getAutoCommit()) {
            commit();
        }
        targetConnection.setAutoCommit(autoCommit);
    }
 
    // 報告當前分支事務是否成功,讓TC發起其他分支回滾或提交請求,重試次數默認爲5次
    private void report(boolean commitDone) throws SQLException {
        int retry = REPORT_RETRY_COUNT;
        while (retry > 0) {
            try {
                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);
                }
            }
        }
    }
}

五、ConnectionContext

ConnectionContext持有當前連接數據對象,xid當前全球事務唯一id,沒有全球事務爲null,branchId當前事務連接分支,受全球事務管控。isGlobalLockRequire鎖,lockKeysBuffer當前操作的表和主鍵字符串拼接而成的字符串集合。SQLUndoLog當前業務處理生成的待回滾日誌。

public class ConnectionContext {
    private String xid;
    private Long branchId;
    private boolean isGlobalLockRequire;
    //table and primary key should not be duplicated
    private Set<String> lockKeysBuffer = new HashSet<>();
    private List<SQLUndoLog> sqlUndoItemsBuffer = new ArrayList<>();

    boolean isGlobalLockRequire() {
        return isGlobalLockRequire;
    }

    void setGlobalLockRequire(boolean isGlobalLockRequire) {
        this.isGlobalLockRequire = isGlobalLockRequire;
    }

    void appendLockKey(String lockKey) {
        lockKeysBuffer.add(lockKey);
    }

    void appendUndoItem(SQLUndoLog sqlUndoLog) {
        sqlUndoItemsBuffer.add(sqlUndoLog);
    }

    public boolean inGlobalTransaction() {
        return xid != null;
    }

    public boolean isBranchRegistered() {
        return branchId != null;
    }

    void bind(String xid) {
        if (xid == null) {
            throw new IllegalArgumentException("xid should not be null");
        }
        if (!inGlobalTransaction()) {
            setXid(xid);
        } else {
            if (!this.xid.equals(xid)) {
                throw new ShouldNeverHappenException();
            }
        }
    }

    public boolean hasUndoLog() {
        return sqlUndoItemsBuffer.size() > 0;
    }

    public String getXid() {
        return xid;
    }
    void setXid(String xid) {
        this.xid = xid;
    }

    public Long getBranchId() {
        return branchId;
    }

    void setBranchId(Long branchId) {
        this.branchId = branchId;
    }

    void reset(){
        this.reset(null);
    }

    void reset(String xid) {
        this.xid = xid;
        branchId = null;
        this.isGlobalLockRequire = false;
        lockKeysBuffer.clear();
        sqlUndoItemsBuffer.clear();
    }

    public String buildLockKeys() {
        if (lockKeysBuffer.isEmpty()) {
            return null;
        }
        StringBuilder appender = new StringBuilder();
        Iterator<String> iterable = lockKeysBuffer.iterator();
        while (iterable.hasNext()) {
            appender.append(iterable.next());
            if (iterable.hasNext()) {
                appender.append(";");
            }
        }
        return appender.toString();
    }

    public List<SQLUndoLog> getUndoItems() {
        return sqlUndoItemsBuffer;
    }
}

六、StatementProxy

StatementProxy繼承AbstractStatementProxy類,泛型繼承Statement,可以爲PreparedStatement或者爲CallableStatement,目前只提供了PreparedStatement的實現,未提供CallableStatement調用存儲過程的實現。AbstractStatementProxy代理statement的方法,只有當executeBatch方法如果是開啓了全球事務,則不支持。StatementProxy的executeQuery,executeUpdate和execute執行sql方法,都是調用ExecuteTemplate的execute模板方法,對sql進行解析,添加日誌處理等工作。StatementCallback回調方法爲原生statement的對應方法。

public class StatementProxy<T extends Statement> extends AbstractStatementProxy<T> {

    public StatementProxy(AbstractConnectionProxy connectionWrapper, T targetStatement, String targetSQL)
        throws SQLException {
        super(connectionWrapper, targetStatement, targetSQL);
    }

    public StatementProxy(AbstractConnectionProxy connectionWrapper, T targetStatement) throws SQLException {
        this(connectionWrapper, targetStatement, null);
    }

    @Override
    public ConnectionProxy getConnectionProxy() {
        return (ConnectionProxy) super.getConnectionProxy();
    }

    @Override
    public ResultSet executeQuery(String sql) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<ResultSet, T>() {
            @Override
            public ResultSet execute(Statement statement, Object... args) throws SQLException {
                return statement.executeQuery((String) args[0]);
            }
        }, sql);
    }

    @Override
    public int executeUpdate(String sql) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<Integer, T>() {
            @Override
            public Integer execute(Statement statement, Object... args) throws SQLException {
                return statement.executeUpdate((String) args[0]);
            }
        }, sql);
    }

    @Override
    public boolean execute(String sql) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<Boolean, T>() {
            @Override
            public Boolean execute(T statement, Object... args) throws SQLException {
                return statement.execute((String) args[0]);
            }
        }, sql);
    }

    @Override
    public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<Integer, T>() {
            @Override
            public Integer execute(T statement, Object... args) throws SQLException {
                return statement.executeUpdate((String) args[0],(int)args[1]);
            }
        }, sql,autoGeneratedKeys);
    }

    @Override
    public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<Integer, T>() {
            @Override
            public Integer execute(T statement, Object... args) throws SQLException {
                return statement.executeUpdate((String) args[0],(int [])args[1]);
            }
        }, sql,columnIndexes);
    }

    @Override
    public int executeUpdate(String sql, String[] columnNames) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<Integer, T>() {
            @Override
            public Integer execute(T statement, Object... args) throws SQLException {
                return statement.executeUpdate((String) args[0],(String[])args[1]);
            }
        }, sql,columnNames);
    }

    @Override
    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<Boolean, T>() {
            @Override
            public Boolean execute(T statement, Object... args) throws SQLException {
                return statement.execute((String) args[0],(int)args[1]);
            }
        }, sql,autoGeneratedKeys);
    }

    @Override
    public boolean execute(String sql, int[] columnIndexes) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<Boolean, T>() {
            @Override
            public Boolean execute(T statement, Object... args) throws SQLException {
                return statement.execute((String) args[0],(int[])args[1]);
            }
        }, sql,columnIndexes);
    }

    @Override
    public boolean execute(String sql, String[] columnNames) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<Boolean, T>() {
            @Override
            public Boolean execute(T statement, Object... args) throws SQLException {
                return statement.execute((String) args[0],(String[])args[1]);
            }
        }, sql,columnNames);
    }
}

七、PreparedStatementProxy

PreparedStatementProxy繼承AbstractPreparedStatementProxy,AbstractPreparedStatementProxy繼承StatementProxy<PreparedStatement>類,實現PreparedStatement和ParametersHolder接口。ParametersHolder持有當前PreparedStatement設置的參數,爲模板方法的sql解析和日誌生成提供方便。執行execute,executeQuery和executeUpdate同樣調用模板方法。

public class PreparedStatementProxy extends AbstractPreparedStatementProxy
    implements PreparedStatement, ParametersHolder {

    @Override
    public ArrayList<Object>[] getParameters() {
        return parameters;
    }

    private void init() throws SQLException {
        int paramCount = targetStatement.getParameterMetaData().getParameterCount();
        this.parameters = new ArrayList[paramCount];
        for (int i = 0; i < paramCount; i++) {
            parameters[i] = new ArrayList<>();
        }
    }

    public PreparedStatementProxy(AbstractConnectionProxy connectionProxy, PreparedStatement targetStatement,
                                  String targetSQL) throws SQLException {
        super(connectionProxy, targetStatement, targetSQL);
        init();
    }

    @Override
    public boolean execute() throws SQLException {
        return ExecuteTemplate.execute(this, new StatementCallback<Boolean, PreparedStatement>() {
            @Override
            public Boolean execute(PreparedStatement statement, Object... args) throws SQLException {
                return statement.execute();
            }
        });
    }

    @Override
    public ResultSet executeQuery() throws SQLException {
        return ExecuteTemplate.execute(this, new StatementCallback<ResultSet, PreparedStatement>() {
            @Override
            public ResultSet execute(PreparedStatement statement, Object... args) throws SQLException {
                return statement.executeQuery();
            }
        });
    }

    @Override
    public int executeUpdate() throws SQLException {
        return ExecuteTemplate.execute(this, new StatementCallback<Integer, PreparedStatement>() {
            @Override
            public Integer execute(PreparedStatement statement, Object... args) throws SQLException {
                return statement.executeUpdate();
            }
        });
    }
}

八、ExecuteTemplate

ExecuteTemplate爲具體statement的execute,executeQuery和executeUpdate執行提供模板方法。

public class ExecuteTemplate {

    public static <T, S extends Statement> T execute(StatementProxy<S> statementProxy,
                                                     StatementCallback<T, S> statementCallback,
                                                     Object... args) throws SQLException {
        return execute(null, statementProxy, statementCallback, args);
    }

    public static <T, S extends Statement> T execute(SQLRecognizer sqlRecognizer,
                                                     StatementProxy<S> statementProxy,
                                                     StatementCallback<T, S> statementCallback,
                                                     Object... args) throws SQLException {

        // 如果當前context沒有開啓全球事務,而且沒有全球鎖,則回調targetStatement執行原生方法
        if (!RootContext.inGlobalTransaction() && !RootContext.requireGlobalLock()) {
            // Just work as original statement
            return statementCallback.execute(statementProxy.getTargetStatement(), args);
        }
	// 通過SQLVisitorFactory找到sqlRecognizer
        if (sqlRecognizer == null) {
            sqlRecognizer = SQLVisitorFactory.get(
                    statementProxy.getTargetSQL(),
                    statementProxy.getConnectionProxy().getDbType());
        }
        Executor<T> executor = null;
        if (sqlRecognizer == null) {
            executor = new PlainExecutor<T, S>(statementProxy, statementCallback);
        } else {
	    // 根據不同的sql類型獲取不同的Executor執行器。
            switch (sqlRecognizer.getSQLType()) {
                case INSERT:
                    executor = new InsertExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
                    break;
                case UPDATE:
                    executor = new UpdateExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
                    break;
                case DELETE:
                    executor = new DeleteExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
                    break;
                case SELECT_FOR_UPDATE:
                    executor = new SelectForUpdateExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
                    break;
                default:
                    executor = new PlainExecutor<T, S>(statementProxy, statementCallback);
                    break;
            }
        }
        T rs = null;
        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;
    }
}

 

發佈了40 篇原創文章 · 獲贊 43 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章