Seata架构篇 - SAGA模式
创始人
2025-05-29 03:05:40

SAGA模式

Saga 模式是 Seata 提供的长事务解决方案,在 Saga 模式中,业务流程中每个参与者都提交本地事务,当某个参与者出现失败,则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都有业务开发实现。

Saga模式示意图

适用场景

业务流程长、业务流程多。

参与者包含其它公司或者遗留系统服务,无法提供 TCC 模式要求的三个接口。

原理

SEATA提供的Saga模式是基于状态机引擎来实现的,机制是:

  1. 通过状态图来定义服务调用的流程并生成 json 状态语言定义文件
  2. 状态图中一个节点可以是调用一个服务,节点可以配置它的补偿节点
  3. 状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚

注意: 异常发生时是否进行补偿也可由用户自定义决定

  1. 可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能

使用

@GetMapping("/test_saga")
public String testSaga() {Map startParams = new HashMap<>(3);String businessKey = String.valueOf(System.currentTimeMillis());startParams.put("businessKey", businessKey);startParams.put("count", 10);startParams.put("amount", new BigDecimal("100"));// 模拟compensate//startParams.put("mockReduceBalanceFail", "true");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());return "finished";
}

在 resources/statelang 目录下创建一个 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"}}
}

BalanceAction

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

BalanceActionImpl

@Service("balanceAction")
public class BalanceActionImpl implements BalanceAction {private static final Logger LOGGER = LoggerFactory.getLogger(BalanceActionImpl.class);@Overridepublic boolean reduce(String businessKey, BigDecimal amount, Map 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;}@Overridepublic boolean compensateReduce(String businessKey, Map 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;}
}

InventoryAction

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

InventoryActionImpl

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

ITAccountService

public interface ITAccountService extends IService {ObjectResponse decreaseAccount(AccountDTO accountDTO);void testGlobalLock();
}

TAccountServiceImpl

@Service
public class TAccountServiceImpl extends ServiceImpl implements ITAccountService {@Overridepublic ObjectResponse decreaseAccount(AccountDTO accountDTO) {int account = baseMapper.decreaseAccount(accountDTO.getUserId(), accountDTO.getAmount().doubleValue());ObjectResponse response = new ObjectResponse<>();if (account > 0) {response.setStatus(RspStatusEnum.SUCCESS.getCode());response.setMessage(RspStatusEnum.SUCCESS.getMessage());return response;}response.setStatus(RspStatusEnum.FAIL.getCode());response.setMessage(RspStatusEnum.FAIL.getMessage());return response;}@Override@GlobalLock@Transactional(rollbackFor = {Throwable.class})public void testGlobalLock() {baseMapper.testGlobalLock("1");System.out.println("Hi, i got lock, i will do some thing with holding this lock.");}
}
                
            
            
            

相关内容

热门资讯

从“冰景”到“智景” 科技加持... 央视网消息:12月17日上午10时,黑龙江哈尔滨冰雪大世界迎来开园。从这个冰雪季开始,大家就在关注这...
麦积山的微笑 麦积山秋景。 核心提示 在甘肃天水市东南部的小陇山林海之中,一座孤峰拔地而起,因其形状酷似农家麦垛,...
明月刀:星期三,上黄山 百字文 星期三,上黄山 ■ 明月刀 我有一个朋友姓高,在黄山工作多年,现在退了。他给我说了一个笑话。...
苏州银行“出海全链通”品牌重磅... 12月16日下午,“汇聚金融活水 润泽中阿园区”跨境人民币创新实践对接会在苏州银行成功举办。本次活动...
首次登顶!昭通文旅抖音号荣膺云... 近日,2025年11月云南文旅抖音影响力排行榜正式发布。本期榜单延续三大指数体系:州(市)级文旅抖音...