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

127 阅读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);
}