文章23 | 阅读 16645 | 点赞0
本文基于seata 1.3.0版本
1987年普林斯顿大学的Hector Garcia-Molina和Kenneth Salem发表了一篇Paper Sagas的论文,讲述了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表的数据是在启动的时候或者启动前写入数据库的,其他两张表的数据都是在事务开始运行后保存到数据库。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_38308374/article/details/108907413
内容来源于网络,如有侵权,请联系作者删除!