河南电商网站设计,ppt模板怎么做 下载网站,福建凭祥建设工程有限公司网站,外贸业务网络推广大家好#xff0c;我是君哥。
状态机在我们的工作中应用非常广泛#xff0c;今天聊一聊分布式事务中间件 Seata 中 Saga 模式的状态机。
1 状态机简介
状态机是一个数学模型#xff0c;它将工作中的运行状态和流转规则抽象出来#xff0c;可以协调相关信号来完成预先设定…大家好我是君哥。
状态机在我们的工作中应用非常广泛今天聊一聊分布式事务中间件 Seata 中 Saga 模式的状态机。
1 状态机简介
状态机是一个数学模型它将工作中的运行状态和流转规则抽象出来可以协调相关信号来完成预先设定的操作。
下面介绍状态机中的几个概念 状态状态机目前的状态标识 状态转移定义状态之间的转移路由 动作Action状态转移需要的操作 事件要执行某个操作时的触发器或者口令。
状态机一般用在状态类型比较多超过 3 个分支流程比较多初始状态经过多个流程的流转达到最终状态的场景。
2 Saga 模式
Saga 模式是分布式事务中长事务的一种解决方案Seata 中 Saga 模式的理论基础是 Hector Kenneth 在 1987 年发表的论文 Sagas。下图来自官网是 Seata 中 Saga 模型 在 Saga 模式中如果一部分分支事务已经提交成功当其中一个分支事务提交失败状态机就会触发所有提交成功的分支事务进行回滚。
分支事务中提交和回滚的逻辑需要由业务代码来实现。
3 Saga 实现
Seata 中 Saga 模式是基于状态机来实现的使用 Saga 模式时先画一张状态图这个状态图定义服务调用流程每个节点调用一个分支事务并且每个节点需要配备一个补偿节点用于分支事务失败后的补偿动作。
以经典电商案例来讲一个分布式事务中有三个分支事务参数者
分支事务动作状态订单服务保存订单保存成功、失败账户服务扣减金额扣减成功、失败库存服务扣减库存扣减成功、失败
在这个分布式事务中只有订单、账户、库存这三个分支事务都提交成功整个事务才能成功。每一个分支事务提交失败其他执行成功的事务都需要反向补偿。如下图 比如扣减金额这个分支事务失败了需要反向补偿扣减金额、保存订单这两个分支事务。那 Seata 是怎么做到事件触发、状态流转和补偿操作的呢
使用 Seata 状态机首先需要定义一个 Json 文件这个 Json 文件把图中的每个节点都定义成一个 StateState 的类型共有四种 ServiceTask对应分支事务的提交操作 Choice对应流程中下一个 State 的选择 CompensationTrigger触发补偿服务 Succeed成功状态当所有分支事务都成功后才会流转到这个状态 Fail失败状态。
3.1 ServiceTask
下面我们看保存订单这个状态
SaveOrder: {Type: ServiceTask,ServiceName: orderSave,ServiceMethod: saveOrder,CompensateState: DeleteOrder,Next: ChoiceAccountState,Input: [$.[businessKey],$.[order]],Output: {SaveOrderResult: $.#root},Status: {#root true: SU,#root false: FA,$Exception{java.lang.Throwable}: UN},Catch: [{Exceptions: [java.lang.Throwable],Next: CompensationTrigger}]
},这个 State 的类型是 ServiceTask上面图中的分支服务和补偿服务都是这种类型也对应代码中的一个 Service。上面的 Json 中主要定义了三个内容 这个 state 调用的 Service 方法 提交失败后的补偿 State(CompensateState) 提交成功后应该跳转的下一个 State(ChoiceAccountState)。
3.2 Choice
下面来看 ChoiceAccountState 这个状态节点Json 文件定义如下
ChoiceAccountState:{Type: Choice,Choices:[{Expression:[SaveOrderResult] true,Next:ReduceAccount}],Default:Fail
}对应的下个节点是 ReduceAccount如果失败就会跳转 Fail 状态。
3.3 Fail
上面 orderSave 这个状态节点如果发生异常会跳转到 CompensationTriggerCompensationTrigger 状态节点定义如下
CompensationTrigger: {Type: CompensationTrigger,Next: Fail
}这个节点会触发 SaveOrder 中定义的补偿服务然后将最终状态流转到 Fail。同时我们也看到只要到了 CompensationTrigger 这个状态节点最终状态就会流转到 Fail。
下面我们把整个 Json 文件的定义贴出来看一下
{Name: buyGoodsOnline,Comment: buy a goods on line, add order, deduct account, deduct storage ,StartState: SaveOrder,Version: 0.0.1,#定义状态States: {SaveOrder: {Type: ServiceTask,ServiceName: orderSave,ServiceMethod: saveOrder,CompensateState: DeleteOrder,Next: ChoiceAccountState,Input: [$.[businessKey],$.[order]],Output: {SaveOrderResult: $.#root},Status: {#root true: SU,#root false: FA,$Exception{java.lang.Throwable}: UN},Catch: [{Exceptions: [java.lang.Throwable],Next: CompensationTrigger}]},ChoiceAccountState:{Type: Choice,Choices:[{Expression:[SaveOrderResult] true,Next:ReduceAccount}],Default:Fail},ReduceAccount: {Type: ServiceTask,ServiceName: accountService,ServiceMethod: decrease,CompensateState: CompensateReduceAccount,Next: ChoiceStorageState,Input: [$.[businessKey],$.[userId],$.[money],{throwException : $.[mockReduceAccountFail]}],Output: {ReduceAccountResult: $.#root},Status: {#root true: SU,#root false: FA,$Exception{java.lang.Throwable}: UN},Catch: [{Exceptions: [java.lang.Throwable],Next: CompensationTrigger}]},ChoiceStorageState:{Type: Choice,Choices:[{Expression:[ReduceAccountResult] true,Next:ReduceStorage}],Default:Fail},ReduceStorage: {Type: ServiceTask,ServiceName: storageService,ServiceMethod: decrease,CompensateState: CompensateReduceStorage,Input: [$.[businessKey],$.[productId],$.[count],{throwException : $.[mockReduceStorageFail]}],Output: {ReduceStorageResult: $.#root},Status: {#root true: SU,#root false: FA,$Exception{java.lang.Throwable}: UN},Catch: [{Exceptions: [java.lang.Throwable],Next: CompensationTrigger}],Next: Succeed},DeleteOrder: {Type: ServiceTask,ServiceName: orderSave,ServiceMethod: deleteOrder,Input: [$.[businessKey],$.[order]]},CompensateReduceAccount: {Type: ServiceTask,ServiceName: accountService,ServiceMethod: compensateDecrease,Input: [$.[businessKey],$.[userId],$.[money]]},CompensateReduceStorage: {Type: ServiceTask,ServiceName: storageService,ServiceMethod: compensateDecrease,Input: [$.[businessKey],$.[productId],$.[count]]},CompensationTrigger: {Type: CompensationTrigger,Next: Fail},Succeed: {Type:Succeed},Fail: {Type:Fail,ErrorCode: PURCHASE_FAILED,Message: purchase failed}}
}上面 Json 文件中定义的 buyGoodsOnline是状态机加载的入口状态机会找到这个 name然后把状态加载到自己的内存中。下面我们再来总结一下电商案例中分布式事务状态流转过程 4 状态机应用
上面的电商例子中三个分支服务分别定义了三个 State对应的 ServiceMethod 如下 SaveOrder#saveOrder
public boolean saveOrder(String businessKey, Order order) {logger.info(保存订单, businessKey{}, order: {}, businessKey, order);orderDao.create(order);return true;
}ReduceAccount#decrease
public boolean decrease(String businessKey, Long userId, BigDecimal money) {return accountApi.decrease(businessKey, userId, money);
}ReduceStorage#decrease
public boolean decrease(String businessKey, Long productId, Integer count) {return storageApi.decrease(businessKey, productId, count);
}状态机在启动的时候需要把上面方法中的参数都传入实例代码如下
StateMachineEngine stateMachineEngine (StateMachineEngine) ApplicationContextUtils.getApplicationContext().getBean(stateMachineEngine);
MapString, Object startParams new HashMap(3);
String businessKey String.valueOf(System.currentTimeMillis());
startParams.put(businessKey, businessKey);
startParams.put(order, order);
startParams.put(mockReduceAccountFail, true);
startParams.put(userId, order.getUserId());
startParams.put(money, order.getPayAmount());
startParams.put(productId, order.getProductId());
startParams.put(count, order.getCount());
//这里采用同步方法
StateMachineInstance inst stateMachineEngine.startWithBusinessKey(buyGoodsOnline, null, businessKey, startParams);5 状态机原理
下面这张图来自于 Seata 官网主要讲解了状态机的工作原理 状态机启动时首先启动了全局事务 将状态机的参数记录在本地 seata_state_machine_inst 表 向 Seata Server 注册分支事务 执行 StateA 并记录状态到本地数据库同时会产生路由事件放入 EventQueue执行 StateB 时取出路由消息触发执行。同样 StateB 执行时也会产生路由消息放入 EventQueue 从 EventQueue 取出路由消息执行 StateC 状态机结束流程提交或回滚全局事务。
6 高可用
Seata 中的状态机并不是独立部署而是内嵌在应用中由于状态机上下文和执行日志都记录在本地数据库中所以状态机本身是无状态的。
状态机启动时会发送状态到 Seata Server当一个应用宕机后Seata Server 能感知到并会把恢复请求发送到存活的实例收到请求的实例从数据库取出状态机上下文和执行日志进行恢复。如下图 7 总结
本文讲解了分布式事务中间件 Seata 给 Saga 模式设计的状态机使用方式和原理。状态机在我们的日常工作中使用非常广泛希望 Seata 的设计能对我们设计状态机提供思路和参考。