Saga 模式是 Seata 提供的长事务解决方案,在 Saga 模式中,业务流程中每个参与者都提交本地事务,当某个参与者出现失败,则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都有业务开发实现。
业务流程长、业务流程多。
参与者包含其它公司或者遗留系统服务,无法提供 TCC 模式要求的三个接口。
SEATA提供的Saga模式是基于状态机引擎来实现的,机制是:
注意: 异常发生时是否进行补偿也可由用户自定义决定
@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