Seata的AT模式基本上能满足我们使用分布式事务80%的需求,但涉及不支持事务的数据库与中间件(如redis)等的操作,或AT模式暂未支持的数据库(目前AT支持Mysql、Oracle与PostgreSQL)、跨公司服务的调用、跨语言的应用调用或有手动控制整个二阶段提交过程的需求,则需要结合TCC模式。不仅如此,TCC模式还支持与AT模式混合使用。
1、TCC模式的概念
一个分布式的全局事务,整体是两阶段提交Try-[Comfirm/Cancel] 的模型。在Seata中,AT模式与TCC模式事实上都是两阶段提交
的具体实现。他们的区别在于:
-
AT 模式基于支持本地 ACID (原子性、一致性、隔离性和持久性)事务 的 关系型数据库(目前支持Mysql、Oracle与PostgreSQL):
- 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
- 二阶段 commit 行为:马上成功结束,自动异步批量清理回滚日志。
- 二阶段 rollback 行为:通过回滚日志,自动生成补偿操作,完成数据回滚。
-
TCC 模式,不依赖于底层数据资源的事务支持,
需要人为编写代码实现提交和回滚操作
:- 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑(实际业务逻辑)。
- 二阶段 commit 行为:调用 自定义的 commit 逻辑。
- 二阶段 rollback 行为:调用 自定义的 rollback 逻辑。
所谓 TCC 模式,是指支持把 自定义 的分支事务的提交和回滚纳入到全局事务的管理中。
通俗讲,SEATA的TCC模式就是手工的AT模式,它允许你自定义两阶段的处理逻辑而不依赖AT模式的undo_log。
2、业务代码处理
对业务接口进行定义处理。
-
seata.jar、配置文件正常配置即可,可以参考Seata分布式事物(一)
-
在业务接口实例
import io.seata.rm.tcc.api.BusinessActionContext; import io.seata.rm.tcc.api.BusinessActionContextParameter; import io.seata.rm.tcc.api.LocalTCC; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; import java.math.BigDecimal; @LocalTCC public interface AccountService { /** * 扣除余额 * 定义两阶段提交 * name = reduceStock 为一阶段try方法 * commitMethod = commitTcc 为二阶段确认方法 * rollbackMethod = cancel 为二阶段取消方法 * BusinessActionContextParameter注解 递参数到二阶段方法中 * * @param userId 用户ID * @param money 扣减金额 */ @TwoPhaseBusinessAction(name = "reduceBalance", commitMethod = "commitTcc", rollbackMethod = "cancelTcc") void reduceBalance(@BusinessActionContextParameter(paramName = "userId") Integer userId, @BusinessActionContextParameter(paramName = "money") BigDecimal money); /** * 确认方法、可以另命名,但要保证与@TwoPhaseBusinessAction中 commitMethod一致 * context可以传递try方法的参数 * * @param context 上下文 * @return boolean */ boolean commitTcc(BusinessActionContext context); /** * 二阶段取消方法 * * @param context 上下文 * @return boolean */ boolean cancelTcc(BusinessActionContext context); }
-
业务实现
import com.baomidou.dynamic.datasource.annotation.DS; import com.bjpowernode.mapper.AccountMapper; import com.bjpowernode.model.Account; import com.bjpowernode.service.AccountService; import io.seata.core.context.RootContext; import io.seata.rm.tcc.api.BusinessActionContext; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; @Slf4j @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountMapper accountMapper; @Override public void reduceBalance(Integer userId, BigDecimal money) { log.info("当前 XID: {}", RootContext.getXID()); // TODO 实际业务逻辑,未发生异常走commitMethod 方法,异常走rollbackMethod } /** * tcc服务(confirm)方法 * 可以空确认 * * @param context 上下文 * @return boolean */ @Override public boolean commitTcc(BusinessActionContext context) { log.info("Confirm阶段,AccountServiceImpl, commitTcc --> xid = {}", context.getXid() + ", commitTcc提交成功"); return true; } /** * tcc服务(cancel)方法 * * @param context 上下文 * @return boolean */ @DS(value = "account-ds") @Override public boolean cancelTcc(BusinessActionContext context) { log.info("Cancel阶段,AccountServiceImpl, cancelTcc --> xid = " + context.getXid() + ", cancelTcc提交失败"); //TODO 这里可以实现中间件、非关系型数据库的回滚操作 return true; } }
-
业务调用
@GlobalTransactional //seata全局事务注解, TM 事务发起方 public Integer createOrder(Integer userId, BigDecimal money) throws Exception { // 减余额 accountService.reduceBalance(userId, money); // 其他业务逻辑,业务逻辑中某一个步骤发生异常触发全部回滚 return 123; }