Seata源码—8.Seata Saga模式的事务处理一

0 阅读23分钟

大纲

1.Seata Saga案例简介

2.Seata Saga案例的状态机定义分析

3.Seata Saga分布式事务与状态机关系

4.Seata Saga案例的Dubbo服务调用配置分析

5.Seata Saga案例的状态机数据库和工程启动

6.基于数据库的状态机配置实例的初始化

7.状态机配置实例中包含的一些关键组件

8.默认的状态机配置类的初始化

9.状态机定义的仓储组件解析状态机定义文件

10.状态机定义的仓储组件注册StateMachine

11.状态机引擎接口的源码

12.状态机引擎创建状态机实例的源码

13.ProcessContextBuilder构建流程上下文

14.StateLogStore记录日志和开启Saga全局事务

15.状态操作组件获取状态和State状态类继承体系

16.启动状态机实例时发布流程上下文到事件总线

17.通过业务处理器处理状态机当前需要执行的状态

18.通过服务调用组件来执行State指定的服务方法

19.将业务处理器路由到状态机下一个要执行的状态


1.Seata Saga案例简介

seata-samples项目下的saga模块,会基于Seata Saga模式,实现了分布式事务的提交和回滚。

在dubbo-saga-sample模块中,一个分布式事务内会有两个Saga事务参与者,这两个Saga事务参与者分别是InventoryAction和BalanceAction。当分布式事务提交时,两者均提交。当分布式事务回滚时,两者均回滚。

这两个Saga事务参与者均是Dubbo服务。这两个Saga事务参与者都有一个reduce方法表示库存扣减或余额扣减,还有一个compensateReduce方法表示补偿扣减操作。

InventoryAction接口定义如下:

public interface InventoryAction {
    boolean reduce(String businessKey, int count);
    boolean compensateReduce(String businessKey);
}

BalanceAction接口定义如下:

public interface BalanceAction {
    boolean reduce(String businessKey, BigDecimal amount, Map<String, Object> params);
    boolean compensateReduce(String businessKey, Map<String, Object> params);
}

运行dubbo-saga-sample模块的Demo工程:

步骤一:启动Seata Server

步骤二:运行DubboSagaProviderStarter

步骤三:运行DubboSagaTransactionStarter

2.Seata Saga案例的状态机定义分析

(1)Saga状态机定义文件详情

(2)状态机定义的全流程输入输出和流转分析

(1)Saga状态机定义文件详情

Saga的本质就是一个状态机。

reduce_inventory_and_balance.json

{
    "Name": "reduceInventoryAndBalance",
    "Comment": "reduce inventory then reduce balance in a transaction",
    "StartState": "ReduceInventory",
    "Version": "0.0.1",
    "States": {
        "ReduceInventory": {
            "Type": "ServiceTask",
            "ServiceName": "inventoryAction",
            "ServiceMethod": "reduce",
            "CompensateState": "CompensateReduceInventory",
            "Next": "ChoiceState",
            "Input": [
                "$.[businessKey]",
                "$.[count]"
            ],
            "Output": {
                "reduceInventoryResult": "$.#root"
            },
            "Status": {
                "#root == true": "SU",
                "#root == false": "FA",
                "$Exception{java.lang.Throwable}": "UN"
            }
        },
        "ChoiceState": {
            "Type": "Choice",
            "Choices": [{
                "Expression": "[reduceInventoryResult] == true",
                "Next": "ReduceBalance"
            }],
            "Default": "Fail"
        },
        "ReduceBalance": {
            "Type": "ServiceTask",
            "ServiceName": "balanceAction",
            "ServiceMethod": "reduce",
            "CompensateState": "CompensateReduceBalance",
            "Input": [
                "$.[businessKey]",
                "$.[amount]",
                {
                    "throwException": "$.[mockReduceBalanceFail]"
                }
            ],
            "Output": {
                "compensateReduceBalanceResult": "$.#root"
            },
            "Status": {
                "#root == true": "SU",
                "#root == false": "FA",
                "$Exception{java.lang.Throwable}": "UN"
            },
            "Catch": [{
                "Exceptions": [
                    "java.lang.Throwable"
                ],
                "Next": "CompensationTrigger"
            }],
            "Next": "Succeed"
        },
        "CompensateReduceInventory": {
            "Type": "ServiceTask",
            "ServiceName": "inventoryAction",
            "ServiceMethod": "compensateReduce",
            "Input": [
                "$.[businessKey]"
            ]
        },
        "CompensateReduceBalance": {
            "Type": "ServiceTask",
            "ServiceName": "balanceAction",
            "ServiceMethod": "compensateReduce",
            "Input": [
                "$.[businessKey]"
            ]
        },
        "CompensationTrigger": {
            "Type": "CompensationTrigger",
            "Next": "Fail"
        },
        "Succeed": {
            "Type": "Succeed"
        },
        "Fail": {
            "Type": "Fail",
            "ErrorCode": "PURCHASE_FAILED",
            "Message": "purchase failed"
        }
    }
}

(2)状态机定义的全流程输入输出和流转分析

3.Seata Saga分布式事务与状态机关系

Seata Saga分布式事务基本都是通过状态机一起配合起来使用的,通过状态机来调度编排每一个分布式事务的执行。编排的每一个服务执行时,可能会执行本地事务,也可能会调用远程服务。触发每一个服务的补偿时,可能会回滚本地事务,也可能会补偿远程服务。

4.Seata Saga案例的Dubbo服务调用配置分析

(1)Dubbo服务提供者的配置

(2)Dubbo服务调用者的配置

(3)Seata Saga状态机的Bean的配置

(1)Dubbo服务提供者的配置

seata-dubbo-provider.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd" default-autowire="byName">
    <dubbo:application name="saga-sample">
        <dubbo:parameter key="qos.enable" value="true"/>
        <dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
        <dubbo:parameter key="qos.port" value="33333"/>
    </dubbo:application>

    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="-1"/>
    <dubbo:provider timeout="10000" threads="10" threadpool="fixed" loadbalance="roundrobin"/>
    
    <dubbo:service ref="inventoryActionImpl" interface="io.seata.samples.saga.action.InventoryAction"/>
    <dubbo:service ref="balanceActionImpl" interface="io.seata.samples.saga.action.BalanceAction"/>

    <bean id="inventoryActionImpl" class="io.seata.samples.saga.action.impl.InventoryActionImpl"/>
    <bean id="balanceActionImpl" class="io.seata.samples.saga.action.impl.BalanceActionImpl"/>
</beans>

(2)Dubbo服务调用者的配置

seata-dubbo-reference.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd" default-autowire="byName">
    <dubbo:application name="saga-sample-reference">
        <dubbo:parameter key="qos.enable" value="true"/>
        <dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
        <dubbo:parameter key="qos.port" value="22222"/>
    </dubbo:application>

    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:protocol name="dubbo" port="-1"/>

    <dubbo:reference id="inventoryAction" interface="io.seata.samples.saga.action.InventoryAction" check="false" lazy="true"/>
    <dubbo:reference id="balanceAction" interface="io.seata.samples.saga.action.BalanceAction" check="false" lazy="true"/>
</beans>

(3)Seata Saga状态机的Bean的配置

seata-saga.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"
       default-autowire="byName">

    <bean id="dataSource" class="org.h2.jdbcx.JdbcConnectionPool" destroy-method="dispose">
        <constructor-arg>
            <bean class="org.h2.jdbcx.JdbcDataSource">
                <property name="URL" value="jdbc:h2:mem:seata_saga"/>
                <property name="user" value="sa"/>
                <property name="password" value="sa"/>
            </bean>
        </constructor-arg>
    </bean>

    <jdbc:initialize-database data-source="dataSource">
        <jdbc:script location="classpath:sql/h2_init.sql"/>
    </jdbc:initialize-database>

    <bean id="stateMachineEngine" class="io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine">
        <property name="stateMachineConfig" ref="dbStateMachineConfig"></property>
    </bean>
    
    <!-- DB是用来保存流程定义去启动状态机的流程实例、记录状态机运行时各个节点的情况 -->
    <bean id="dbStateMachineConfig" class="io.seata.saga.engine.config.DbStateMachineConfig">
        <property name="dataSource" ref="dataSource"></property>
        <property name="resources" value="statelang/*.json"></property>
        <property name="enableAsync" value="true"></property>
        <property name="threadPoolExecutor" ref="threadExecutor"></property>
        <property name="applicationId" value="saga_sample"></property>
        <property name="txServiceGroup" value="my_test_tx_group"></property>
    </bean>
    
    <bean id="threadExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean">
        <property name="threadNamePrefix" value="SAGA_ASYNC_EXE_"/>
        <property name="corePoolSize" value="1"/>
        <property name="maxPoolSize" value="20"/>
    </bean>
    
    <bean class="io.seata.saga.rm.StateMachineEngineHolder">
        <property name="stateMachineEngine" ref="stateMachineEngine"/>
    </bean>
</beans>

5.Seata Saga案例的状态机数据库和工程启动

(1)状态机相关的数据库表

(2)Seata Saga案例的工程启动

(1)状态机相关的数据库表

状态机定义 -> 状态机实例 -> 状态实例

-- PUBLIC.SEATA_STATE_INST definition

-- 状态实例,每个状态机实例里会有多个状态实例
CREATE CACHED TABLE "PUBLIC"."SEATA_STATE_INST"(
    "ID" VARCHAR NOT NULL COMMENT 'id',
    "MACHINE_INST_ID" VARCHAR NOT NULL COMMENT 'state machine instance id,机器实例ID',
    "NAME" VARCHAR NOT NULL COMMENT 'state name',
    "TYPE" VARCHAR COMMENT 'state type',
    "SERVICE_NAME" VARCHAR COMMENT 'service name',
    "SERVICE_METHOD" VARCHAR COMMENT 'method name',
    "SERVICE_TYPE" VARCHAR COMMENT 'service type',
    "BUSINESS_KEY" VARCHAR COMMENT 'business key',
    "STATE_ID_COMPENSATED_FOR" VARCHAR COMMENT 'state compensated for',
    "STATE_ID_RETRIED_FOR" VARCHAR COMMENT 'state retried for',
    "GMT_STARTED" TIMESTAMP NOT NULL COMMENT 'start time',
    "IS_FOR_UPDATE" TINYINT COMMENT 'is service for update',
    "INPUT_PARAMS" CLOB COMMENT 'input parameters',
    "OUTPUT_PARAMS" CLOB COMMENT 'output parameters',
    "STATUS" VARCHAR NOT NULL COMMENT 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
    "EXCEP" BLOB COMMENT 'exception',
    "GMT_UPDATED" TIMESTAMP COMMENT 'update time',
    "GMT_END" TIMESTAMP COMMENT 'end time'
);

-- PUBLIC.SEATA_STATE_MACHINE_DEF definition
-- 状态机定义,在json文件中定义好的状态流程会存放到这里
CREATE CACHED TABLE "PUBLIC"."SEATA_STATE_MACHINE_DEF"(
    "ID" VARCHAR NOT NULL COMMENT 'id',
    "NAME" VARCHAR NOT NULL COMMENT 'name',
    "TENANT_ID" VARCHAR NOT NULL COMMENT 'tenant id',
    "APP_NAME" VARCHAR NOT NULL COMMENT 'application name',
    "TYPE" VARCHAR COMMENT 'state language type',
    "COMMENT_" VARCHAR COMMENT 'comment',
    "VER" VARCHAR NOT NULL COMMENT 'version',
    "GMT_CREATE" TIMESTAMP NOT NULL COMMENT 'create time',
    "STATUS" VARCHAR NOT NULL COMMENT 'status(AC:active|IN:inactive)',
    "CONTENT" CLOB COMMENT 'content',
    "RECOVER_STRATEGY" VARCHAR COMMENT 'transaction recover strategy(compensate|retry)'
);

-- PUBLIC.SEATA_STATE_MACHINE_INST definition
-- 状态机实例,每执行一次完整的分布式事务就会对应一个状态机实例,每个状态机实例里会有多个状态实例
CREATE CACHED TABLE "PUBLIC"."SEATA_STATE_MACHINE_INST"(
    "ID" VARCHAR NOT NULL COMMENT 'id',
    "MACHINE_ID" VARCHAR NOT NULL COMMENT 'state machine definition id',
    "TENANT_ID" VARCHAR NOT NULL COMMENT 'tenant id',
    "PARENT_ID" VARCHAR COMMENT 'parent id',
    "GMT_STARTED" TIMESTAMP NOT NULL COMMENT 'start time',
    "BUSINESS_KEY" VARCHAR COMMENT 'business key',
    "START_PARAMS" CLOB COMMENT 'start parameters',
    "GMT_END" TIMESTAMP COMMENT 'end time',
    "EXCEP" BLOB COMMENT 'exception',
    "END_PARAMS" CLOB COMMENT 'end parameters',
    "STATUS" VARCHAR COMMENT 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
    "COMPENSATION_STATUS" VARCHAR COMMENT 'compensation status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
    "IS_RUNNING" TINYINT COMMENT 'is running(0 no|1 yes)',
    "GMT_UPDATED" TIMESTAMP NOT NULL
);

(2)Seata Saga案例的工程启动

ApplicationKeeper启动完之后就会被阻塞住。

public class ApplicationKeeper {
    private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationKeeper.class);
    private final ReentrantLock LOCK = new ReentrantLock();
    private final Condition STOP = LOCK.newCondition();

    //Instantiates a new Application keeper.
    public ApplicationKeeper(AbstractApplicationContext applicationContext) {
        addShutdownHook(applicationContext);
    }

    private void addShutdownHook(final AbstractApplicationContext applicationContext) {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    applicationContext.close();
                    LOGGER.info("ApplicationContext " + applicationContext + " is closed.");
                } catch (Exception e) {
                    LOGGER.error("Failed to close ApplicationContext", e);
                }
                try {
                    LOCK.lock();
                    STOP.signal();
                } finally {
                    LOCK.unlock();
                }
            }
        }));
    }

    public void keep() {
        synchronized (LOCK) {
            try {
                LOGGER.info("Application is keep running ... ");
                LOCK.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

步骤一:运行DubboSagaProviderStarter,启动Dubbo Provider。

public class DubboSagaProviderStarter {
    private static TestingServer server;

    //The entry point of application.
    public static void main(String[] args) throws Exception {
        //mock zk server
        mockZKServer();
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
            new String[]{"spring/seata-dubbo-provider.xml"}
        );
        new ApplicationKeeper(applicationContext).keep();
    }

    private static void mockZKServer() throws Exception {
        //Mock zk server,作为Dubbo配置中心
        server = new TestingServer(2181, true);
        server.start();
    }
}

public class InventoryActionImpl implements InventoryAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(InventoryActionImpl.class);
    
    @Override
    public boolean reduce(String businessKey, int count) {
        LOGGER.info("reduce inventory succeed, count: " + count + ", businessKey:" + businessKey);
        return true;
    }
    
    @Override
    public boolean compensateReduce(String businessKey) {
        LOGGER.info("compensate reduce inventory succeed, businessKey:" + businessKey);
        return true;
    }
}

public class BalanceActionImpl implements BalanceAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(BalanceActionImpl.class);
    
    @Override
    public boolean reduce(String businessKey, BigDecimal amount, Map<String, Object> params) {
        if (params != null) {
            Object throwException = params.get("throwException");
            if (throwException != null && "true".equals(throwException.toString())) {
                throw new RuntimeException("reduce balance failed");
            }
        }
        LOGGER.info("reduce balance succeed, amount: " + amount + ", businessKey:" + businessKey);
        return true;
    }

    @Override
    public boolean compensateReduce(String businessKey, Map<String, Object> params) {
        if (params != null) {
            Object throwException = params.get("throwException");
            if (throwException != null && "true".equals(throwException.toString())) {
                throw new RuntimeException("compensate reduce balance failed");
            }
        }
        LOGGER.info("compensate reduce balance succeed, businessKey:" + businessKey);
        return true;
    }
}

步骤二:运行DubboSagaTransactionStarter,启动Demo工程。

public class DubboSagaTransactionStarter {
    private static volatile Object lock = new Object();
    
    private static AsyncCallback CALL_BACK = new AsyncCallback() {
        @Override
        public void onFinished(ProcessContext context, StateMachineInstance stateMachineInstance) {
            synchronized (lock) {
                lock.notifyAll();
            }
        }
        
        @Override
        public void onError(ProcessContext context, StateMachineInstance stateMachineInstance, Exception exp) {
            synchronized (lock) {
                lock.notifyAll();
            }
        }
    };

    public static void main(String[] args) {
        AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext(
            new String[]{"spring/seata-saga.xml", "spring/seata-dubbo-reference.xml"}
        );
        StateMachineEngine stateMachineEngine = (StateMachineEngine) applicationContext.getBean("stateMachineEngine");
        transactionCommittedDemo(stateMachineEngine);
        transactionCompensatedDemo(stateMachineEngine);
        new ApplicationKeeper(applicationContext).keep();
    }

    private static void transactionCommittedDemo(StateMachineEngine stateMachineEngine) {
        //设置状态机上下文
        Map<String, Object> startParams = new HashMap<>(3);
        String businessKey = String.valueOf(System.currentTimeMillis());
        startParams.put("businessKey", businessKey);
        startParams.put("count", 10);
        startParams.put("amount", new BigDecimal("100"));

        //sync test,同步启动状态机
        StateMachineInstance inst = stateMachineEngine.startWithBusinessKey("reduceInventoryAndBalance", null, businessKey, startParams);
        Assert.isTrue(ExecutionStatus.SU.equals(inst.getStatus()), "saga transaction execute failed. XID: " + inst.getId());
        System.out.println("saga transaction commit succeed. XID: " + inst.getId());

        //async test,异步启动状态机
        businessKey = String.valueOf(System.currentTimeMillis());
        inst = stateMachineEngine.startWithBusinessKeyAsync("reduceInventoryAndBalance", null, businessKey, startParams, CALL_BACK);

        waittingForFinish(inst);

        Assert.isTrue(ExecutionStatus.SU.equals(inst.getStatus()), "saga transaction execute failed. XID: " + inst.getId());
        System.out.println("saga transaction commit succeed. XID: " + inst.getId());
    }

    private static void transactionCompensatedDemo(StateMachineEngine stateMachineEngine) {
        //设置状态机上下文
        Map<String, Object> startParams = new HashMap<>(4);
        String businessKey = String.valueOf(System.currentTimeMillis());
        startParams.put("businessKey", businessKey);
        startParams.put("count", 10);
        startParams.put("amount", new BigDecimal("100"));
        startParams.put("mockReduceBalanceFail", "true");

        //sync test,同步启动状态机
        StateMachineInstance inst = stateMachineEngine.startWithBusinessKey("reduceInventoryAndBalance", null, businessKey, startParams);

        //async test,异步启动状态机
        businessKey = String.valueOf(System.currentTimeMillis());
        inst = stateMachineEngine.startWithBusinessKeyAsync("reduceInventoryAndBalance", null, businessKey, startParams, CALL_BACK);

        waittingForFinish(inst);

        Assert.isTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()), "saga transaction compensate failed. XID: " + inst.getId());
        System.out.println("saga transaction compensate succeed. XID: " + inst.getId());
    }

    private static void waittingForFinish(StateMachineInstance inst) {
        synchronized (lock) {
            if (ExecutionStatus.RU.equals(inst.getStatus())) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6.基于数据库的状态机配置实例的初始化

基于数据库的状态机配置实例DbStateMachineConfig会将状态机定义、状态机实例、状态实例放在数据库中。由seata-saga.xml配置文件可知,它会被注入到状态机引擎实例StateMachineEngine里。DbStateMachineConfig的初始化源码如下:

//DbStateMachineConfig表示状态机定义、状态机实例、状态实例会放在DB中
public class DbStateMachineConfig extends DefaultStateMachineConfig implements DisposableBean {
    //会通过seata-saga.xml配置文件,注入一个数据库连接池进来,这个DB会用来存放状态机定义、状态机实例、状态实例
    private DataSource dataSource;
    //应用程序ID
    private String applicationId;
    //分布式事务服务的分组
    private String txServiceGroup;
    //DB里存放状态机数据的表的前缀
    private String tablePrefix = "seata_";
    //DB的类型,seata-samples里用的是h2,一般都用MySQL
    private String dbType;
    //Saga分布式事务模版
    private SagaTransactionalTemplate sagaTransactionalTemplate;
    //是否启用RM资源管理器上报成功的机制,默认是不启用的
    private boolean rmReportSuccessEnable = DEFAULT_CLIENT_REPORT_SUCCESS_ENABLE;
    //是否启用Saga分支事务的注册机制,默认是不启用的
    private boolean sagaBranchRegisterEnable = DEFAULT_CLIENT_SAGA_BRANCH_REGISTER_ENABLE;

    //初始化
    public DbStateMachineConfig() {
        try {
            Configuration configuration = ConfigurationFactory.getInstance();
            if (configuration != null) {
                this.rmReportSuccessEnable = configuration.getBoolean(ConfigurationKeys.CLIENT_REPORT_SUCCESS_ENABLE, DEFAULT_CLIENT_REPORT_SUCCESS_ENABLE);
                this.sagaBranchRegisterEnable = configuration.getBoolean(ConfigurationKeys.CLIENT_SAGA_BRANCH_REGISTER_ENABLE, DEFAULT_CLIENT_SAGA_BRANCH_REGISTER_ENABLE);
                //设置Saga状态机定义文件的解析器
                setSagaJsonParser(configuration.getConfig(ConfigurationKeys.CLIENT_SAGA_JSON_PARSER, DEFAULT_SAGA_JSON_PARSER));
                this.applicationId = configuration.getConfig(ConfigurationKeys.APPLICATION_ID);
                this.txServiceGroup = configuration.getConfig(ConfigurationKeys.TX_SERVICE_GROUP);
                //设置Saga重试持久化模式是否为更新模式
                setSagaRetryPersistModeUpdate(configuration.getBoolean(ConfigurationKeys.CLIENT_SAGA_RETRY_PERSIST_MODE_UPDATE, DEFAULT_CLIENT_SAGA_RETRY_PERSIST_MODE_UPDATE));
                //设置Saga补偿持久化模式是否为更新模式
                setSagaCompensatePersistModeUpdate(configuration.getBoolean(ConfigurationKeys.CLIENT_SAGA_COMPENSATE_PERSIST_MODE_UPDATE, DEFAULT_CLIENT_SAGA_COMPENSATE_PERSIST_MODE_UPDATE));
            }
        } catch (Exception e) {
            LOGGER.warn("Load SEATA configuration failed, use default configuration instead.", e);
        }
    }

    //根据数据库连接池获取DB类型
    public static String getDbTypeFromDataSource(DataSource dataSource) throws SQLException {
        try (Connection con = dataSource.getConnection()) {
            DatabaseMetaData metaData = con.getMetaData();
            return metaData.getDatabaseProductName();
        }
    }

    //初始化DB状态机配置DbStateMachineConfig
    //因为DbStateMachineConfig本身就是Spring Bean,当该Bean初始化完毕后,就会执行afterPropertiesSet()方法
    @Override
    public void afterPropertiesSet() throws Exception {
        //先根据数据库连接池获取DB类型
        dbType = getDbTypeFromDataSource(dataSource);
        //如果状态机实例日志和状态实例日志存储组件为null
        if (getStateLogStore() == null) {
            DbAndReportTcStateLogStore dbStateLogStore = new DbAndReportTcStateLogStore();
            dbStateLogStore.setDataSource(dataSource);
            dbStateLogStore.setTablePrefix(tablePrefix);
            dbStateLogStore.setDbType(dbType);
            dbStateLogStore.setDefaultTenantId(getDefaultTenantId());
            dbStateLogStore.setSeqGenerator(getSeqGenerator());
    
            if (StringUtils.hasLength(getSagaJsonParser())) {
                ParamsSerializer paramsSerializer = new ParamsSerializer();
                paramsSerializer.setJsonParserName(getSagaJsonParser());
                dbStateLogStore.setParamsSerializer(paramsSerializer);
            }

            if (sagaTransactionalTemplate == null) {
                DefaultSagaTransactionalTemplate defaultSagaTransactionalTemplate = new DefaultSagaTransactionalTemplate();
                defaultSagaTransactionalTemplate.setApplicationContext(getApplicationContext());
                defaultSagaTransactionalTemplate.setApplicationId(applicationId);
                defaultSagaTransactionalTemplate.setTxServiceGroup(txServiceGroup);
                defaultSagaTransactionalTemplate.afterPropertiesSet();
                sagaTransactionalTemplate = defaultSagaTransactionalTemplate;
            }

            dbStateLogStore.setSagaTransactionalTemplate(sagaTransactionalTemplate);
            setStateLogStore(dbStateLogStore);
        }

        if (getStateLangStore() == null) {
            DbStateLangStore dbStateLangStore = new DbStateLangStore();
            dbStateLangStore.setDataSource(dataSource);
            dbStateLangStore.setTablePrefix(tablePrefix);
            dbStateLangStore.setDbType(dbType);
            setStateLangStore(dbStateLangStore);
        }

        super.afterPropertiesSet();//must execute after StateLangStore initialized
    }

    @Override
    public void destroy() throws Exception {
        if ((sagaTransactionalTemplate != null) && (sagaTransactionalTemplate instanceof DisposableBean)) {
            ((DisposableBean) sagaTransactionalTemplate).destroy();
        }
    }
    ...
}

//Default state machine configuration
//默认的状态机配置,DB状态机配置DbStateMachineConfig是它的子类
//DefaultStateMachineConfig可以支持不同种类的状态机配置
//即状态机定义、状态机实例、状态实例,既可以放在DB里,也可以放在其他存储里,默认是放在DB里的

//DefaultStateMachineConfig会封装(注入)状态机运行时需要的所有组件
//StateMachineConfig可以获取到状态机需要的各种组件
//ApplicationContextAware可以感知Spring容器上下文
//InitializingBean可以对Spring Bean进行初始化
public class DefaultStateMachineConfig implements StateMachineConfig, ApplicationContextAware, InitializingBean {
    ...
    ...
}

7.状态机配置实例中包含的一些关键组件

(1)状态机配置组件

(2)状态机实例和状态实例的仓储组件

(3)状态机实例日志和状态实例日志的存储组件

(4)状态机定义的存储组件

(5)表达式工厂管理器

(6)状态机定义的仓储组件

(1)状态机配置组件

StateMachineConfig表示的是状态机配置组件,可以从中获取状态机需要的各种组件。

//StateMachineConfig
//状态机配置组件接口,可以获取到状态机需要的各种组件
public interface StateMachineConfig {
    //Gets state log store.
    //获取状态机实例和状态实例的仓储组件
    //StateLogRepository可以根据各种条件查询状态机实例和状态实例
    StateLogRepository getStateLogRepository();

    //Gets get state log store.
    //获取状态机实例日志和状态实例日志的存储组件
    //StateLogStore可以对状态机日志和状态日志进行读写、也可以根据各种条件查询状态机实例和状态实例
    StateLogStore getStateLogStore();

    //Gets get state language definition store.
    //获取状态机定义的存储组件
    StateLangStore getStateLangStore();

    //Gets get expression factory manager.
    //获取表达式工厂管理器
    ExpressionFactoryManager getExpressionFactoryManager();

    //Gets get evaluator factory manager.
    //获取表达式计算工厂管理器
    EvaluatorFactoryManager getEvaluatorFactoryManager();

    //Gets get charset.
    //获取字符集编码
    String getCharset();

    //Gets get default tenant id.
    //获取默认的租户ID
    String getDefaultTenantId();

    //Gets get state machine repository.
    //获取状态机定义的仓储组件
    StateMachineRepository getStateMachineRepository();

    //Gets get status decision strategy.
    //获取State执行结果的判定策略组件
    StatusDecisionStrategy getStatusDecisionStrategy();

    //Gets get seq generator.
    //获取序号生成器
    SeqGenerator getSeqGenerator();

    //Gets get process ctrl event publisher.
    //获取流程控制事件的发布器(同步发布)
    ProcessCtrlEventPublisher getProcessCtrlEventPublisher();

    //Gets get async process ctrl event publisher.
    //获取流程控制事件的发布器(异步发布)
    ProcessCtrlEventPublisher getAsyncProcessCtrlEventPublisher();

    //Gets get application context.
    //获取Spring容器上下文
    ApplicationContext getApplicationContext();

    //Gets get thread pool executor.
    //获取异步执行的线程池
    ThreadPoolExecutor getThreadPoolExecutor();

    //Is enable async boolean.
    //是否启用异步
    boolean isEnableAsync();

    //get ServiceInvokerManager
    //获取服务调用的管理器
    ServiceInvokerManager getServiceInvokerManager();

    //get trans operation timeout
    //获取事务操作的超时时间
    int getTransOperationTimeout();

    //get service invoke timeout
    //获取服务调用的超时时间
    int getServiceInvokeTimeout();

    //get ScriptEngineManager
    //获取脚本引擎管理器
    ScriptEngineManager getScriptEngineManager();
}

(2)状态机实例和状态实例的仓储组件

StateLogRepository表示的是状态机实例和状态实例的仓储组件,可以根据不同的条件查询状态机实例和状态实例。

//State Log Repository
//状态机实例和状态实例的仓储组件,可以根据各种条件查询状态机实例和状态实例
public interface StateLogRepository {
    //Get state machine instance
    //根据状态机实例ID,获取到具体的状态机实例
    StateMachineInstance getStateMachineInstance(String stateMachineInstanceId);

    //Get state machine instance by businessKey
    //根据业务key获取状态机实例
    StateMachineInstance getStateMachineInstanceByBusinessKey(String businessKey, String tenantId);

    //Query the list of state machine instances by parent id
    //根据父ID查询状态机实例
    List<StateMachineInstance> queryStateMachineInstanceByParentId(String parentId);

    //Get state instance
    //根据状态实例ID查询状态实例
    StateInstance getStateInstance(String stateInstanceId, String machineInstId);

    //Get a list of state instances by state machine instance id
    //根据状态机实例ID查询所有状态实例
    List<StateInstance> queryStateInstanceListByMachineInstanceId(String stateMachineInstanceId);
}

(3)状态机实例日志和状态实例日志的存储组件接口

StateLogStore表示的是状态机实例日志和状态实例日志的存储组件,可以记录各种状态机实例日志和状态实例日志。

//StateMachine engine log store
//状态机实例日志和状态实例日志的存储组件
public interface StateLogStore {
    //Record state machine startup events
    //记录状态机实例的启动事件日志
    void recordStateMachineStarted(StateMachineInstance machineInstance, ProcessContext context);

    //Record status end event
    //记录状态机实例的完成事件日志
    void recordStateMachineFinished(StateMachineInstance machineInstance, ProcessContext context);

    //Record state machine restarted
    //记录状态机实例的重启事件日志
    void recordStateMachineRestarted(StateMachineInstance machineInstance, ProcessContext context);

    //Record state start execution event
    //记录状态实例的启动事件日志
    void recordStateStarted(StateInstance stateInstance, ProcessContext context);

    //Record state execution end event
    //记录状态实例的完成事件日志
    void recordStateFinished(StateInstance stateInstance, ProcessContext context);

    //Get state machine instance
    //根据状态机实例ID获取状态机实例
    StateMachineInstance getStateMachineInstance(String stateMachineInstanceId);

    //Get state machine instance by businessKey
    //根据业务key获取状态机实例
    StateMachineInstance getStateMachineInstanceByBusinessKey(String businessKey, String tenantId);

    //Query the list of state machine instances by parent id
    //根据父ID查询状态机实例
    List<StateMachineInstance> queryStateMachineInstanceByParentId(String parentId);

    //Get state instance
    //根据状态实例ID和状态机ID获取状态实例
    StateInstance getStateInstance(String stateInstanceId, String machineInstId);

    //Get a list of state instances by state machine instance id
    //根据状态机实例ID查询状态实例
    List<StateInstance> queryStateInstanceListByMachineInstanceId(String stateMachineInstanceId);
}

(4)状态机定义的存储组件接口

StateLangStore表示的是状态机定义的存储组件,可以存储和获取不同的状态机定义。

//State language definition store
//状态机定义的存储组件
public interface StateLangStore {
    //Query the state machine definition by id
    //根据状态机ID获取状态机定义
    StateMachine getStateMachineById(String stateMachineId);

    //Get the latest version of the state machine by state machine name
    //根据状态机名称和租户ID,可以获取最新版本的状态机定义
    StateMachine getLastVersionStateMachine(String stateMachineName, String tenantId);

    //Storage state machine definition
    //存储状态机定义
    boolean storeStateMachine(StateMachine stateMachine);
}

(5)表达式工厂管理器

ExpressionFactoryManager表示的是表达式工厂管理器,可以根据不同的表达式类型获取不同的表达式工厂组件。

//Expression factory manager
//表达式工厂管理器
public class ExpressionFactoryManager {
    public static final String DEFAULT_EXPRESSION_TYPE = "Default";
    //表达式类型 -> 表达式工厂
    private Map<String, ExpressionFactory> expressionFactoryMap = new ConcurrentHashMap<>();
    
    //根据不同的表达式类型,去获取不同的表达式工厂组件
    public ExpressionFactory getExpressionFactory(String expressionType) {
        if (StringUtils.isBlank(expressionType)) {
            expressionType = DEFAULT_EXPRESSION_TYPE;
        }
        return expressionFactoryMap.get(expressionType);
    }
    
    public void setExpressionFactoryMap(Map<String, ExpressionFactory> expressionFactoryMap) {
        this.expressionFactoryMap.putAll(expressionFactoryMap);
    }
    
    public void putExpressionFactory(String type, ExpressionFactory factory) {
        this.expressionFactoryMap.put(type, factory);
    }
}

(6)状态机定义的仓储组件

StateMachineRepository表示的是状态机定义的仓储组件,可以注册和获取一个状态机定义。

//StateMachineRepository
//状态机定义的仓储组件
public interface StateMachineRepository {
    //Gets get state machine by id.
    //根据状态机ID获取一个状态机定义StateMachine
    StateMachine getStateMachineById(String stateMachineId);

    //Gets get state machine.
    //根据状态机名字、租户ID获取一个状态机定义StateMachine
    StateMachine getStateMachine(String stateMachineName, String tenantId);

    //Gets get state machine.
    //根据状态机名字、租户ID、版本号获取一个状态机定义StateMachine
    StateMachine getStateMachine(String stateMachineName, String tenantId, String version);

    //Register the state machine to the repository (if the same version already exists, return the existing version)
    //向状态机定义的仓储组件注册一个状态机定义
    StateMachine registryStateMachine(StateMachine stateMachine);

    //registry by resources
    //向状态机定义的仓储组件注册资源
    void registryByResources(Resource[] resources, String tenantId) throws IOException;
}

8.默认的状态机配置类的初始化

DefaultStateMachineConfig是默认的状态机配置类,基于数据库的状态机配置组件DbStateMachineConfig是它的子类。它可以支持不同种类的状态机配置,也就是状态机定义、状态机实例、状态实例,既可以放在数据库里,也可以放在其他存储里,默认是放在数据库里的。

//Default state machine configuration
//DefaultStateMachineConfig会封装(注入)状态机运行时需要的所有组件
//StateMachineConfig可以获取到状态机需要的各种组件
//ApplicationContextAware可以感知Spring容器上下文
//InitializingBean可以对Spring Bean进行初始化
public class DefaultStateMachineConfig implements StateMachineConfig, ApplicationContextAware, InitializingBean {
    private static final int DEFAULT_TRANS_OPER_TIMEOUT     = 60000 * 30;
    private static final int DEFAULT_SERVICE_INVOKE_TIMEOUT = 60000 * 5;

    //事务操作的超时时间,默认是30分钟
    private int transOperationTimeout = DEFAULT_TRANS_OPER_TIMEOUT;
    //服务调用的超时时间,默认是5分钟
    private int serviceInvokeTimeout  = DEFAULT_SERVICE_INVOKE_TIMEOUT;

    //状态机实例和状态实例的仓储组件
    private StateLogRepository stateLogRepository;
    //状态机实例日志和状态实例日志的存储组件
    private StateLogStore stateLogStore;
    //状态机定义的存储组件
    private StateLangStore stateLangStore;
    //表达式工厂管理器
    private ExpressionFactoryManager expressionFactoryManager;
    //表达式计算的工厂管理器
    private EvaluatorFactoryManager evaluatorFactoryManager;
    //状态机定义的仓储组件
    private StateMachineRepository stateMachineRepository;
    //State执行结果的判定策略组件
    private StatusDecisionStrategy statusDecisionStrategy;
    //序号生成器
    private SeqGenerator seqGenerator;

    //流程控制的事件发布器(同步发布)
    private ProcessCtrlEventPublisher syncProcessCtrlEventPublisher;
    //流程控制的事件发布器(异步发布)
    private ProcessCtrlEventPublisher asyncProcessCtrlEventPublisher;
    //Spring容器上下文
    private ApplicationContext applicationContext;
    //异步执行的线程池
    private ThreadPoolExecutor threadPoolExecutor;
    //是否启用异步执行,默认是false
    private boolean enableAsync = false;
    //服务调用的管理器
    private ServiceInvokerManager serviceInvokerManager;

    //是否自动注册资源(自动注册流程定义),默认是true
    private boolean autoRegisterResources = true;
    //默认的状态机定义文件(json文件)
    private String[] resources = new String[]{"classpath*:seata/saga/statelang/**/*.json"};
    //字符集编码
    private String charset = "UTF-8";
    //默认租户ID
    private String defaultTenantId = "000001";
    //脚本引擎管理器
    private ScriptEngineManager scriptEngineManager;
    //状态机定义文件(json文件)解析器
    private String sagaJsonParser = DEFAULT_SAGA_JSON_PARSER;
    //是否更新Saga重试持久化的模式
    private boolean sagaRetryPersistModeUpdate = DEFAULT_CLIENT_SAGA_RETRY_PERSIST_MODE_UPDATE;
    //是否更新Saga补偿持久化的模式
    private boolean sagaCompensatePersistModeUpdate = DEFAULT_CLIENT_SAGA_COMPENSATE_PERSIST_MODE_UPDATE;

    //因为DefaultStateMachineConfig实现了InitializingBean接口
    //所以继承DefaultStateMachineConfig的Spring Bean初始化之后,就会回调afterPropertiesSet()方法
    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }

    //初始化方法
    protected void init() throws Exception {
        //创建表达式工厂管理器
        if (expressionFactoryManager == null) {
            //创建一个表达式工厂管理器
            expressionFactoryManager = new ExpressionFactoryManager();

            //创建一个Spring EL表达式工厂
            SpringELExpressionFactory springELExpressionFactory = new SpringELExpressionFactory();
            //注入(设置)Spring容器上下文到Spring EL表达式工厂中
            springELExpressionFactory.setApplicationContext(getApplicationContext());
            //将Spring EL表达式工厂放入表达式工厂管理器中
            expressionFactoryManager.putExpressionFactory(ExpressionFactoryManager.DEFAULT_EXPRESSION_TYPE, springELExpressionFactory);

            //创建一个序号表达式工厂
            SequenceExpressionFactory sequenceExpressionFactory = new SequenceExpressionFactory();
            //注入(设置)序号生成器到序号表达式工厂中
            sequenceExpressionFactory.setSeqGenerator(getSeqGenerator());
            //将序号表达式工厂放入表达式工厂管理器中
            expressionFactoryManager.putExpressionFactory(DomainConstants.EXPRESSION_TYPE_SEQUENCE, sequenceExpressionFactory);
        }

        //创建表达式计算工厂管理器
        if (evaluatorFactoryManager == null) {
            //创建一个表达式计算工厂管理器
            evaluatorFactoryManager = new EvaluatorFactoryManager();

            //创建一个表达式计算工厂,并将该表达式计算工厂放入表达式计算工厂管理器中
            ExpressionEvaluatorFactory expressionEvaluatorFactory = new ExpressionEvaluatorFactory();
            expressionEvaluatorFactory.setExpressionFactory(expressionFactoryManager.getExpressionFactory(ExpressionFactoryManager.DEFAULT_EXPRESSION_TYPE));
            evaluatorFactoryManager.putEvaluatorFactory(EvaluatorFactoryManager.EVALUATOR_TYPE_DEFAULT, expressionEvaluatorFactory);
            //创建一个异常匹配计算工厂,并将该异常匹配计算工厂放入表达式计算工厂管理器中
            evaluatorFactoryManager.putEvaluatorFactory(DomainConstants.EVALUATOR_TYPE_EXCEPTION, new ExceptionMatchEvaluatorFactory());
        }

        //创建状态机定义的仓储组件
        if (stateMachineRepository == null) {
            StateMachineRepositoryImpl stateMachineRepository = new StateMachineRepositoryImpl();
            stateMachineRepository.setCharset(charset);//设置字符集编码
            stateMachineRepository.setSeqGenerator(seqGenerator);//设置序号生成器
            stateMachineRepository.setStateLangStore(stateLangStore);//设置状态机定义的存储组件
            stateMachineRepository.setDefaultTenantId(defaultTenantId);//设置默认租户ID
            stateMachineRepository.setJsonParserName(sagaJsonParser);//设置状态机定义文件(json文件)解析器
            this.stateMachineRepository = stateMachineRepository;
        }

        //stateMachineRepository may be overridden, so move `stateMachineRepository.registryByResources()` here.
        //如果需要自动注册资源(比如数据库等),则获取资源并进行注册
        if (autoRegisterResources && ArrayUtils.isNotEmpty(resources)) {
            try {
                //读取默认的状态机定义文件(json文件),把这个状态机定义注册到状态机定义的仓储组件中
                Resource[] resources = ResourceUtil.getResources(this.resources);
                stateMachineRepository.registryByResources(resources, defaultTenantId);
            } catch (IOException e) {
                LOGGER.error("Load State Language Resources failed.", e);
            }
        }

        //创建状态机实例和状态实例的仓储组件
        if (stateLogRepository == null) {
            StateLogRepositoryImpl stateLogRepositoryImpl = new StateLogRepositoryImpl();
            stateLogRepositoryImpl.setStateLogStore(stateLogStore);
            this.stateLogRepository = stateLogRepositoryImpl;
        }

        //创建State执行结果的判定策略组件
        if (statusDecisionStrategy == null) {
            statusDecisionStrategy = new DefaultStatusDecisionStrategy();
        }

        //创建流程控制的事件发布器(同步发布)
        if (syncProcessCtrlEventPublisher == null) {
            //创建流程控制的事件发布器
            ProcessCtrlEventPublisher syncEventPublisher = new ProcessCtrlEventPublisher();
            //创建流程控制器
            ProcessControllerImpl processorController = createProcessorController(syncEventPublisher);

            //创建流程控制事件的消费者
            ProcessCtrlEventConsumer processCtrlEventConsumer = new ProcessCtrlEventConsumer();
            processCtrlEventConsumer.setProcessController(processorController);

            //创建事件总线
            DirectEventBus directEventBus = new DirectEventBus();
            syncEventPublisher.setEventBus(directEventBus);

            directEventBus.registerEventConsumer(processCtrlEventConsumer);
            syncProcessCtrlEventPublisher = syncEventPublisher;
        }

        //如果启用了异步化执行 且 流程控制的事件发布器(异步发布)为null
        if (enableAsync && asyncProcessCtrlEventPublisher == null) {
            ProcessCtrlEventPublisher asyncEventPublisher = new ProcessCtrlEventPublisher();
            ProcessControllerImpl processorController = createProcessorController(asyncEventPublisher);

            ProcessCtrlEventConsumer processCtrlEventConsumer = new ProcessCtrlEventConsumer();
            processCtrlEventConsumer.setProcessController(processorController);

            AsyncEventBus asyncEventBus = new AsyncEventBus();
            asyncEventBus.setThreadPoolExecutor(getThreadPoolExecutor());
            asyncEventPublisher.setEventBus(asyncEventBus);

            asyncEventBus.registerEventConsumer(processCtrlEventConsumer);
            asyncProcessCtrlEventPublisher = asyncEventPublisher;
        }

        //创建服务调用管理器
        if (this.serviceInvokerManager == null) {
            this.serviceInvokerManager = new ServiceInvokerManager();

            //创建Spring Bean的服务调用组件
            SpringBeanServiceInvoker springBeanServiceInvoker = new SpringBeanServiceInvoker();
            springBeanServiceInvoker.setApplicationContext(getApplicationContext());
            springBeanServiceInvoker.setThreadPoolExecutor(threadPoolExecutor);
            springBeanServiceInvoker.setSagaJsonParser(getSagaJsonParser());
            //将Spring Bean的服务调用组件放入到服务调用管理器中
            this.serviceInvokerManager.putServiceInvoker(DomainConstants.SERVICE_TYPE_SPRING_BEAN, springBeanServiceInvoker);
        }

        //创建脚本引擎管理器
        if (this.scriptEngineManager == null) {
            this.scriptEngineManager = new ScriptEngineManager();
        }
    }

    protected ProcessControllerImpl createProcessorController(ProcessCtrlEventPublisher eventPublisher) throws Exception {
        //创建状态机流程处理的路由器
        StateMachineProcessRouter stateMachineProcessRouter = new StateMachineProcessRouter();
        stateMachineProcessRouter.initDefaultStateRouters();
        //通过SPI机制加载状态机流程处理路由的拦截器
        loadStateRouterInterceptors(stateMachineProcessRouter.getStateRouters());

        //创建状态机流程处理器
        StateMachineProcessHandler stateMachineProcessHandler = new StateMachineProcessHandler();
        stateMachineProcessHandler.initDefaultHandlers();
        //通过SPI机制加载状态机流程处理器的拦截器
        loadStateHandlerInterceptors(stateMachineProcessHandler.getStateHandlers());

        //创建默认的路由处理器,并设置事件发布器
        DefaultRouterHandler defaultRouterHandler = new DefaultRouterHandler();
        defaultRouterHandler.setEventPublisher(eventPublisher);

        //创建状态机流程处理路由器对应的Map
        Map<String, ProcessRouter> processRouterMap = new HashMap<>(1);
        processRouterMap.put(ProcessType.STATE_LANG.getCode(), stateMachineProcessRouter);
        defaultRouterHandler.setProcessRouters(processRouterMap);

        //创建自定义的业务处理器
        CustomizeBusinessProcessor customizeBusinessProcessor = new CustomizeBusinessProcessor();

        //创建状态机流程处理器对应的Map
        Map<String, ProcessHandler> processHandlerMap = new HashMap<>(1);
        processHandlerMap.put(ProcessType.STATE_LANG.getCode(), stateMachineProcessHandler);
        customizeBusinessProcessor.setProcessHandlers(processHandlerMap);

        //创建路由处理器对应的Map
        Map<String, RouterHandler> routerHandlerMap = new HashMap<>(1);
        routerHandlerMap.put(ProcessType.STATE_LANG.getCode(), defaultRouterHandler);
        customizeBusinessProcessor.setRouterHandlers(routerHandlerMap);

        //创建流程控制器
        ProcessControllerImpl processorController = new ProcessControllerImpl();
        processorController.setBusinessProcessor(customizeBusinessProcessor);

        return processorController;
    }

    protected void loadStateHandlerInterceptors(Map<String, StateHandler> stateHandlerMap) {
        for (StateHandler stateHandler : stateHandlerMap.values()) {
            if (stateHandler instanceof InterceptableStateHandler) {
                InterceptableStateHandler interceptableStateHandler = (InterceptableStateHandler) stateHandler;
                List<StateHandlerInterceptor> interceptorList = EnhancedServiceLoader.loadAll(StateHandlerInterceptor.class);
                for (StateHandlerInterceptor interceptor : interceptorList) {
                    if (interceptor.match(interceptableStateHandler.getClass())) {
                        interceptableStateHandler.addInterceptor(interceptor);
                    }
                    if (interceptor instanceof ApplicationContextAware) {
                        ((ApplicationContextAware) interceptor).setApplicationContext(getApplicationContext());
                    }
                }
            }
        }
    }

    protected void loadStateRouterInterceptors(Map<String, StateRouter> stateRouterMap) {
        for (StateRouter stateRouter : stateRouterMap.values()) {
            if (stateRouter instanceof InterceptableStateRouter) {
                InterceptableStateRouter interceptableStateRouter = (InterceptableStateRouter) stateRouter;
                List<StateRouterInterceptor> interceptorList = EnhancedServiceLoader.loadAll(StateRouterInterceptor.class);
                for (StateRouterInterceptor interceptor : interceptorList) {
                    if (interceptor.match(interceptableStateRouter.getClass())) {
                        interceptableStateRouter.addInterceptor(interceptor);
                    }
                    if (interceptor instanceof ApplicationContextAware) {
                        ((ApplicationContextAware) interceptor).setApplicationContext(getApplicationContext());
                    }
                }
            }
        }
    }
    ...
}

9.状态机定义的仓储组件解析状态机定义文件

在DefaultStateMachineConfig的init()方法初始化逻辑中,会通过调用状态机定义的仓储组件StateMachineRepository.registryByResources()方法来读取和解析状态机定义文件,并把解析出来的状态机定义StateMachine注册到状态机定义的仓储组件中,也就是存储在stateMachineRepository变量里。

注意:StateMachine就是状态机定义。

public class StateMachineRepositoryImpl implements StateMachineRepository {
    ...
    @Override
    public void registryByResources(Resource[] resources, String tenantId) throws IOException {
        if (resources != null) {
            for (Resource resource : resources) {
                //1.基于IO流把状态机定义文件(json文件)读取出来,放入json字符串
                String json;
                try (InputStream is = resource.getInputStream()) {
                    json = IOUtils.toString(is, charset);
                }

                //2.从状态机解析器工厂中获取一个状态机解析器,然后解析json字符串得到一个状态机定义StateMachine
                StateMachine stateMachine = StateMachineParserFactory.getStateMachineParser(jsonParserName).parse(json);
                if (stateMachine != null) {
                    stateMachine.setContent(json);
                    if (StringUtils.isBlank(stateMachine.getTenantId())) {
                        stateMachine.setTenantId(tenantId);
                    }
                    //3.注册状态机定义StateMachine
                    registryStateMachine(stateMachine);
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("===== StateMachine Loaded: \n{}", json);
                    }
                }
            }
        }
    }
    ...
}

10.状态机定义的仓储组件注册StateMachine

public class StateMachineRepositoryImpl implements StateMachineRepository {
    private Map<String/** Name_Tenant **/, Item> stateMachineMapByNameAndTenant = new ConcurrentHashMap<>();
    private Map<String/** Id **/, Item> stateMachineMapById = new ConcurrentHashMap<>();
    private StateLangStore stateLangStore;//状态机定义的存储组件
    ...
    
    @Override
    public StateMachine registryStateMachine(StateMachine stateMachine) {
        String stateMachineName = stateMachine.getName();
        String tenantId = stateMachine.getTenantId();

        if (stateLangStore != null) {
            //1.从状态机定义的存储组件中,根据状态机名称和租户ID,获取最新版本的状态机定义StateMachine
            StateMachine oldStateMachine = stateLangStore.getLastVersionStateMachine(stateMachineName, tenantId);

            if (oldStateMachine != null) {
                byte[] oldBytesContent = null;
                byte[] bytesContent = null;
                try {
                    oldBytesContent = oldStateMachine.getContent().getBytes(charset);
                    bytesContent = stateMachine.getContent().getBytes(charset);
                } catch (UnsupportedEncodingException e) {
                    LOGGER.error(e.getMessage(), e);
                }
                if (Arrays.equals(bytesContent, oldBytesContent) && stateMachine.getVersion() != null && stateMachine.getVersion().equals(oldStateMachine.getVersion())) {
                    LOGGER.info("StateMachine[{}] is already exist a same version", stateMachineName);

                    stateMachine.setId(oldStateMachine.getId());
                    stateMachine.setGmtCreate(oldStateMachine.getGmtCreate());

                    Item item = new Item(stateMachine);
                    stateMachineMapByNameAndTenant.put(stateMachineName + "_" + tenantId, item);
                    stateMachineMapById.put(stateMachine.getId(), item);
                    return stateMachine;
                }
            }
            if (StringUtils.isBlank(stateMachine.getId())) {
                //2.生成新的状态机定义StateMachine的ID
                stateMachine.setId(seqGenerator.generate(DomainConstants.SEQ_ENTITY_STATE_MACHINE));
            }
            stateMachine.setGmtCreate(new Date());
            //3.通过状态机定义的存储组件,把状态机定义StateMachine保存起来,默认就是把状态机定义StateMachine存储到DB中
            stateLangStore.storeStateMachine(stateMachine);
        }

        if (StringUtils.isBlank(stateMachine.getId())) {
            stateMachine.setId(seqGenerator.generate(DomainConstants.SEQ_ENTITY_STATE_MACHINE));
        }

        Item item = new Item(stateMachine);
        //4.把状态机定义放入内存中
        stateMachineMapByNameAndTenant.put(stateMachineName + "_" + tenantId, item);
        stateMachineMapById.put(stateMachine.getId(), item);
        return stateMachine;
    }
    ...
}

public class DbStateLangStore extends AbstractStore implements StateLangStore {
    ...
    @Override
    public boolean storeStateMachine(StateMachine stateMachine) {
        //把状态机定义StateMachine写入到DB中
        return executeUpdate(stateLangStoreSqls.getInsertStateMachineSql(dbType), STATE_MACHINE_TO_STATEMENT, stateMachine) > 0;
    }
    ...
}

11.状态机引擎接口的源码

从seata-saga.xml配置文件可知,基于数据库的状态机配置实例DbStateMachineConfig会被注入到基于流程控制的状态机引擎实例ProcessCtrlStateMachineEngine中。

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"
       default-autowire="byName">
    ...
    <bean id="stateMachineEngine" class="io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine">
        <property name="stateMachineConfig" ref="dbStateMachineConfig"></property>
    </bean>
    ...
</beans>

基于流程控制的状态机引擎ProcessCtrlStateMachineEngine,会实现状态机引擎接口StateMachineEngine。

//ProcessCtrl-based state machine engine
//基于流程控制的状态机引擎
public class ProcessCtrlStateMachineEngine implements StateMachineEngine {
    //需要在seata-saga.xml配置文件中注入状态机配置
    private StateMachineConfig stateMachineConfig;
    ...
}

//State machine engine
//状态机引擎接口
public interface StateMachineEngine {
    //start a state machine instance
    //根据状态机的名称、租户ID、启动参数来启动一个状态机实例
    StateMachineInstance start(String stateMachineName, String tenantId, Map<String, Object> startParams) throws EngineExecutionException;

    //start a state machine instance with businessKey
    //根据状态机的名称、租户ID、业务key、启动参数来启动一个状态机实例
    StateMachineInstance startWithBusinessKey(String stateMachineName, String tenantId, String businessKey, Map<String, Object> startParams) throws EngineExecutionException;

    //start a state machine instance asynchronously
    //根据状态机的名称、租户ID、启动参数来启动一个状态机实例
    //也就是状态机实例跑完之后,会回调传入的callback()方法
    StateMachineInstance startAsync(String stateMachineName, String tenantId, Map<String, Object> startParams, AsyncCallback callback) throws EngineExecutionException;

    //start a state machine instance asynchronously with businessKey
    //根据状态机的名称、租户ID、业务key、启动参数来异步化启动一个状态机实例
    //也就是状态机实例跑完之后,会回调传入的callback()方法
    StateMachineInstance startWithBusinessKeyAsync(String stateMachineName, String tenantId, String businessKey, Map<String, Object> startParams, AsyncCallback callback) throws EngineExecutionException;

    //forward restart a failed state machine instance
    //重启一个失败的状态机实例
    StateMachineInstance forward(String stateMachineInstId, Map<String, Object> replaceParams) throws ForwardInvalidException;

    //forward restart a failed state machine instance asynchronously
    //异步化重启一个失败的状态机实例
    StateMachineInstance forwardAsync(String stateMachineInstId, Map<String, Object> replaceParams, AsyncCallback callback) throws ForwardInvalidException;

    //compensate a state machine instance
    //对一个状态机实例进行补偿
    StateMachineInstance compensate(String stateMachineInstId, Map<String, Object> replaceParams) throws EngineExecutionException;

    //compensate a state machine instance asynchronously
    //对一个状态机实例进行异步化补偿
    StateMachineInstance compensateAsync(String stateMachineInstId, Map<String, Object> replaceParams, AsyncCallback callback) throws EngineExecutionException;

    //skip current failed state instance and forward restart state machine instance
    //跳过当前失败的状态机实例,同时重启一个状态机实例
    StateMachineInstance skipAndForward(String stateMachineInstId, Map<String, Object> replaceParams) throws EngineExecutionException;

    //skip current failed state instance and forward restart state machine instance asynchronously
    StateMachineInstance skipAndForwardAsync(String stateMachineInstId, AsyncCallback callback) throws EngineExecutionException;

    //get state machine configurations
    StateMachineConfig getStateMachineConfig();

    //Reload StateMachine Instance
    StateMachineInstance reloadStateMachineInstance(String instId);
}