Seata解析-saga模式介绍

x33g5p2x  于2021-12-21 转载在 其他  
字(4.3k)|赞(0)|评价(0)|浏览(599)

本文基于seata 1.3.0版本

1987年普林斯顿大学的Hector Garcia-Molina和Kenneth Salem发表了一篇Paper Sagas的论文,讲述了saga模式如何处理长事务。
关于saga的介绍请大家参见文章,写的非常详细:

分布式事务:Saga模式

saga提供了两种实现方式,一种是编排,另一种是控制。seata的实现方式是后者。seata的控制器使用状态机驱动事务执行。
同AT模式,在saga模式下,seata也提供了RM、TM和TC三个角色。TC也是位于sever端,RM和TM位于客户端。TM用于开启全局事务,RM开启分支事务,TC监控事务运行。
在使用saga模式前,我们需要先定义好状态机,seata提供了网址可以可视化编辑状态机:

http://seata.io/saga_designer/index.html

编辑好后,将状态机以JSON格式导出文件(导出还需要我们手工编辑,我没有找到通过页面直接导出的方式)。seata提供了DbStateMachineConfig类解析状态机文件,并将解析好的内容写入数据库。这样状态机的定义以后可以直接从数据库获取。
在saga模式下,一个状态机实例就是一个全局事务,状态机中的每个状态是分支事务。
下面是seata提供的JSON格式的状态机定义例子:

{
    "Name": "reduceInventoryAndBalance",//定义状态机的名字
    "Comment": "reduce inventory then reduce balance in a transaction",
    "StartState": "ReduceInventory",//定义状态机的初始状态,也就是状态机开始运行时第一个执行的状态
    "Version": "0.0.1",
    //下面是状态的定义
    "States": {
        "ReduceInventory": {
            "Type": "ServiceTask",//状态类型,该类型状态表示一个分支事务
            "ServiceName": "inventoryAction",//调用的bean对象名,当前版本只支持spring容器的bean
            "ServiceMethod": "reduce",//调用的方法
            "CompensateState": "CompensateReduceInventory",//如果事务需要回滚,那么就调用该补偿状态
            "Next": "ChoiceState",//当前状态执行成功后,下一个需要执行的状态
            //执行ServiceMethod方法的入参
            "Input": [
                "$.[businessKey]",
                "$.[count]"
            ],
            //ServiceMethod方法的返回值
            "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"
            },
            //如果当前状态执行过程中抛出异常,这里可以捕获异常,并执行Next状态
            "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"
        }
    }
}

下图是saga模式下状态机的运行结构:

事务运行过程中,也可能发生回滚。当全局事务需要回滚时,TC发起回滚请求,seata执行补偿状态。

当然事务执行过程中,也可以发生自动补偿。如下图:

TC通知事务回滚和事务执行失败自动补偿两个场景很类似,都是找到所有要补偿的状态,然后顺次调用对应的补偿逻辑,不同的是,第一个场景执行完所有的补偿状态就结束了,而第二个场景是执行完后还要执行CompensationTrigger类型状态的Next状态。另外补偿逻辑不能有Next状态。补偿逻辑使用的也是普通ServiceTask类型,只不过是逆向逻辑。

从上面这些可以看到,seata提供了事务执行失败后的两种处理方式,一种是不停的重试直到成功,另一种是事务回滚发起状态补偿,状态补偿是在状态机定义中设定好的。
seata在运行的过程中,创建了一个处理栈,所有将要运行的状态都在该栈中,栈顶的状态是下一个要运行的状态,当开始运行栈顶状态时,就将状态从栈中弹出。状态机初始运行时,栈中只有一个初始状态,初始状态运行完毕后切换到下一状态,这时seata将下一状态装入栈中,控制器接下来执行该状态。
在saga模式中,控制器不在TC端,而在应用端,TC只是起到监控的作用,比如监控事务执行是否超时,记录事务执行进度等。应用端通过调用状态机引擎的start方法启动状态机运行,接下来状态机可以根据定义自动切换状态直到事务执行结束。
seata运行时,在数据库中创建seata_state_inst、seata_state_machine_def、seata_state_machine_inst三张表,作用分别是保存状态实例、保存状态机定义、保存状态机实例,其中seata_state_machine_def表的数据是在启动的时候或者启动前写入数据库的,其他两张表的数据都是在事务开始运行后保存到数据库。

相关文章