Seata1.3(TCC模式) + Zookeeper +Dubbo

2 篇文章 0 订阅
2 篇文章 0 订阅

Seata1.3(TCC模式) + Zookeeper +Dubbo 整合

Seata下载

注:TCC模式

AT模式参考前边,有写过

TCC模式的解决方案是最终一致性,通过两个阶段去处理业务,第一步预处理阶段,第二步提交或者回滚

测试代码:

业务场景:发布任务,扣除任务佣金,之后业务异常

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public JsonResult releaseTaskTCC() {
        Task task = Task.getTask();
        //预发布任务
        taskTccAction.prepareCreateOrder(null, task);
        //扣除发布任务所需要的佣金 1块钱
        memberAccountService.updateAccountTCC(task.getStoreId(), MemberAccountConstant.OUT, BigDecimal.ONE);
        //扣除完之后模拟异常,看扣除的钱是否回滚
        int i = 1 / 0;
        return JsonResult.builder().build();
    }

发布任务TCC的两个阶段业务处理接口

/**TCCAction**/
@LocalTCC
public interface TaskTccAction {


    /**
     * 第一阶段的方法,通过注解指定第二阶段的两个方法名
     * @param businessActionContext 上下文对象,用来在两个阶段之间传递数据
     * @param task
     * @return
     */
    @TwoPhaseBusinessAction(name = "orderTccAction", commitMethod = "commit", rollbackMethod = "rollback")
    boolean prepareCreateOrder(BusinessActionContext businessActionContext,@BusinessActionContextParameter(paramName = "task") Task task);




    /**
     * 第二阶段 - 提交
     * @param businessActionContext
     * @return
     */
    boolean commit(BusinessActionContext businessActionContext);


    /**
     * 第二阶段 - 回滚
     * @param businessActionContext
     * @return
     */
    boolean rollback(BusinessActionContext businessActionContext);

}

发布任务TCC的两个阶段业务处理实现

/**
 * 发布任务TCC
 *
 * @author zjj
 */
@Component
@Slf4j
public class TaskTccActionImpl implements TaskTccAction {

    @Resource
    private TaskMapper taskMapper;

    @Resource
    private TaskTxLogMapper taskTxLogMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean prepareCreateOrder(BusinessActionContext businessActionContext, Task task) {

        String xid = businessActionContext.getXid();
        long branchId = businessActionContext.getBranchId();
        log.info("创建 TaskTccActionImpl 第一阶段,预留资源 - xid:{}", xid);
        //幂等判断 判断order_tx_log表中是否有try日志记录,如果有则不再执行
        if (taskTxLogMapper.isExistTaskTxLog(xid, TransactionalStatusEnum.STATUS_TRY.getCode()) > 0) {
            log.info("TaskTccActionImpl try 已经执行,无需重复执行,xid:{}", xid);
            return true;
        }

        //try悬挂处理,如果cancel、confirm有一个已经执行了,try不再执行
        if (taskTxLogMapper.isExistTaskTxLog(xid, TransactionalStatusEnum.STATUS_COMMIT.getCode()) > 0 || taskTxLogMapper.isExistTaskTxLog(xid, TransactionalStatusEnum.STATUS_COMMIT.getCode()) > 0) {
            log.info("TaskTccActionImpl try悬挂处理  cancel或confirm已经执行,不允许执行try,xid:{}", xid);
            return true;
        }
        task.setTxStatus(TransactionalStatusEnum.STATUS_TRY.getCode());
        taskMapper.insert(task);

        //事务成功,保存一个标识,供第二阶段进行判断
        Integer status = TransactionalStatusEnum.STATUS_TRY.getCode();
        TaskTxLog taskTxLog = buildOrderTxLog(xid, branchId, status);
        taskTxLogMapper.insert(taskTxLog);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        long branchId = businessActionContext.getBranchId();
        log.info("创建 TaskTccActionImpl 第二阶段提交,修改订单状态1 - xid:{}", xid);
        // 防止空提交,如果try阶段没有成功,就是一个空提交,直接返回
        if (taskTxLogMapper.isExistTaskTxLog(xid, TransactionalStatusEnum.STATUS_TRY.getCode()) <= 0) {
            log.info("TaskTccActionImpl confirm 空提交...xid:{}", xid);
            return true;
        }

        // 防止幂等性,如果commit阶段重复执行则直接返回
        if (taskTxLogMapper.isExistTaskTxLog(xid, TransactionalStatusEnum.STATUS_COMMIT.getCode()) > 0) {
            log.info("TaskTccActionImpl confirm 已经执行,无需重复执行...xid:{}", xid);
            return true;
        }


        //修改任务状态
        Task task = JSON.parseObject(businessActionContext.getActionContext("task").toString(), Task.class);
        task.setTaskId(task.getTaskId());
        task.setTxStatus(TransactionalStatusEnum.STATUS_COMMIT.getCode());
        taskMapper.updateById(task);

        //增加一条confirm日志,用于幂等、回滚处理
        Integer status = TransactionalStatusEnum.STATUS_COMMIT.getCode();
        TaskTxLog taskTxLog = buildOrderTxLog(xid, branchId, status);
        taskTxLogMapper.insert(taskTxLog);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        long branchId = businessActionContext.getBranchId();
        log.info("创建 TaskTccActionImpl 第二阶段回滚,删除订单 - xid:{}", xid);

        //第一阶段没有完成的情况下,不必执行回滚
        //因为第一阶段有本地事务,事务失败时已经进行了回滚。
        //这就是空回滚
        if (taskTxLogMapper.isExistTaskTxLog(xid, TransactionalStatusEnum.STATUS_TRY.getCode()) <= 0) {
            log.info("TaskTccActionImpl try 没有成功,不必执行回滚,xid:{}", xid);
            return true;
        }
        //幂等性控制:如果重复执行回滚则直接返回
        if (taskTxLogMapper.isExistTaskTxLog(xid, TransactionalStatusEnum.STATUS_ROLLBACK.getCode()) > 0) {
            log.info("TaskTccActionImpl  rollback 不需重复执行回滚,xid:{}", xid);
            return true;
        }
        Task task = JSON.parseObject(businessActionContext.getActionContext("task").toString(), Task.class);
        taskMapper.deleteById(task.getTaskId());

        //回滚结束时,记录回滚标记
        Integer status = TransactionalStatusEnum.STATUS_ROLLBACK.getCode();
        TaskTxLog taskTxLog = buildOrderTxLog(xid, branchId, status);
        taskTxLogMapper.insert(taskTxLog);
        return true;
    }

    /**
     * 构造日志实体
     *
     * @param xid      全局事务id
     * @param branchId 分支事务id
     * @param status   状态
     * @return
     */
    private TaskTxLog buildOrderTxLog(String xid, long branchId, Integer status) {
        TaskTxLog taskTxLog = new TaskTxLog();
        taskTxLog.setBranchId(branchId);
        taskTxLog.setCreateTime(new Date());
        taskTxLog.setUpdateTime(new Date());
        taskTxLog.setStatus(status);
        taskTxLog.setXid(xid);
        return taskTxLog;
    }
}

修改用户资金接口

 @Override
 public JsonResult updateAccountTCC(Long userId, String memberAccountConstant, BigDecimal money) {
        memberAccountTccAction.prepareCreateOrder(null, memberAccountConstant, userId, money);
        return JsonResult.builder().build();
}

修改用户资金TCC的两个阶段接口

/**
 * @author zjj
 */
@LocalTCC
public interface MemberAccountTccAction {


    /**
     * 第一阶段的方法,通过注解指定第二阶段的两个方法名
     *
     * @param businessActionContext 上下文对象,用来在两个阶段之间传递数据
     * @return
     */
    @TwoPhaseBusinessAction(name = "orderTccAction", commitMethod = "commit", rollbackMethod = "rollback")
    boolean prepareCreateOrder(BusinessActionContext businessActionContext,
                               @BusinessActionContextParameter(paramName = "memberAccountConstant")String memberAccountConstant,
                               @BusinessActionContextParameter(paramName = "userId") Long userId,
                               @BusinessActionContextParameter(paramName = "money") BigDecimal money);


    /**
     * 第二阶段 - 提交
     *
     * @param businessActionContext
     * @return
     */
    boolean commit(BusinessActionContext businessActionContext);


    /**
     * 第二阶段 - 回滚
     *
     * @param businessActionContext
     * @return
     */
    boolean rollback(BusinessActionContext businessActionContext);

}

修改用户资金TCC的两个阶段接口实现

@Component
@Slf4j
public class MemberAccountTccActionImpl implements MemberAccountTccAction {

    @Resource
    private IMemberAccountService memberAccountService;

    @Resource
    private MemberAccountTxLogMapper memberAccountTxLogMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean prepareCreateOrder(BusinessActionContext businessActionContext, String memberAccountConstant, Long userId, BigDecimal money) {

        String xid = businessActionContext.getXid();
        long branchId = businessActionContext.getBranchId();
        log.info("创建 任务 第一阶段,预留资源 - xid:{}", xid);
        //幂等判断 判断order_tx_log表中是否有try日志记录,如果有则不再执行
        if (memberAccountTxLogMapper.isExistMemberAccountTxLog(xid, TransactionalStatusEnum.STATUS_TRY.getCode()) > 0) {
            log.info("MemberAccountTccAction try 已经执行,无需重复执行,xid:{}", xid);
            return true;
        }

        //try悬挂处理,如果cancel、confirm有一个已经执行了,try不再执行
        if (memberAccountTxLogMapper.isExistMemberAccountTxLog(xid, TransactionalStatusEnum.STATUS_COMMIT.getCode()) > 0 || memberAccountTxLogMapper.isExistMemberAccountTxLog(xid, TransactionalStatusEnum.STATUS_COMMIT.getCode()) > 0) {
            log.info("MemberAccountTccAction try悬挂处理  cancel或confirm已经执行,不允许执行try,xid:{}", xid);
            return true;
        }

        //一阶段处理
        memberAccountService.handleTxFrozenAmount(userId, money, memberAccountConstant, TXEnum.PRE);

        //事务成功,保存一个标识,供第二阶段进行判断
        Integer status = TransactionalStatusEnum.STATUS_TRY.getCode();
        MemberAccountTxLog memberAccountTxLog = buildMemberAccountTxLog(xid, branchId, status);
        memberAccountTxLogMapper.insert(memberAccountTxLog);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        long branchId = businessActionContext.getBranchId();
        log.info("创建 MemberAccountTccAction 第二阶段提交,修改订单状态1 - xid:{}", xid);
        // 防止空提交,如果try阶段没有成功,就是一个空提交,直接返回
        if (memberAccountTxLogMapper.isExistMemberAccountTxLog(xid, TransactionalStatusEnum.STATUS_TRY.getCode()) <= 0) {
            log.info("MemberAccountTccAction confirm 空提交...xid:{}", xid);
            return true;
        }

        // 防止幂等性,如果commit阶段重复执行则直接返回
        if (memberAccountTxLogMapper.isExistMemberAccountTxLog(xid, TransactionalStatusEnum.STATUS_COMMIT.getCode()) > 0) {
            log.info("MemberAccountTccAction confirm 已经执行,无需重复执行...xid:{}", xid);
            return true;
        }

        //二阶段资金处理  提交操作
        long userId = Long.parseLong(businessActionContext.getActionContext("userId").toString());
        String money = businessActionContext.getActionContext("money").toString();
        String memberAccountConstant = businessActionContext.getActionContext("memberAccountConstant").toString();
        memberAccountService.handleTxFrozenAmount(userId, new BigDecimal(money), memberAccountConstant, TXEnum.COMMIT);

        //增加一条confirm日志,用于幂等、回滚处理
        Integer status = TransactionalStatusEnum.STATUS_COMMIT.getCode();
        MemberAccountTxLog memberAccountTxLog = buildMemberAccountTxLog(xid, branchId, status);
        memberAccountTxLogMapper.insert(memberAccountTxLog);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        long branchId = businessActionContext.getBranchId();
        log.info("创建 MemberAccountTccAction 第二阶段回滚,删除订单 - xid:{}", xid);

        //第一阶段没有完成的情况下,不必执行回滚
        //因为第一阶段有本地事务,事务失败时已经进行了回滚。
        //这就是空回滚
        if (memberAccountTxLogMapper.isExistMemberAccountTxLog(xid, TransactionalStatusEnum.STATUS_TRY.getCode()) <= 0) {
            log.info("MemberAccountTccAction try 没有成功,不必执行回滚,xid:{}", xid);
            return true;
        }
        //幂等性控制:如果重复执行回滚则直接返回
        if (memberAccountTxLogMapper.isExistMemberAccountTxLog(xid, TransactionalStatusEnum.STATUS_ROLLBACK.getCode()) > 0) {
            log.info("MemberAccountTccAction  rollback 不需重复执行回滚,xid:{}", xid);
            return true;
        }

        //二阶段回滚处理
        long userId = Long.parseLong(businessActionContext.getActionContext("userId").toString());
        String money = businessActionContext.getActionContext("money").toString();
        String memberAccountConstant = businessActionContext.getActionContext("memberAccountConstant").toString();
        memberAccountService.handleTxFrozenAmount(userId, new BigDecimal(money), memberAccountConstant, TXEnum.ROLLBACK);

        //回滚结束时,记录回滚标记
        Integer status = TransactionalStatusEnum.STATUS_ROLLBACK.getCode();
        MemberAccountTxLog memberAccountTxLog = buildMemberAccountTxLog(xid, branchId, status);
        memberAccountTxLogMapper.insert(memberAccountTxLog);
        return true;
    }

    /**
     * 构造日志实体
     *
     * @param xid      全局事务id
     * @param branchId 分支事务id
     * @param status   状态
     * @return
     */
    private MemberAccountTxLog buildMemberAccountTxLog(String xid, long branchId, Integer status) {
        MemberAccountTxLog memberAccountTxLog = new MemberAccountTxLog();
        memberAccountTxLog.setBranchId(branchId);
        memberAccountTxLog.setCreateTime(new Date());
        memberAccountTxLog.setUpdateTime(new Date());
        memberAccountTxLog.setStatus(status);
        memberAccountTxLog.setXid(xid);
        return memberAccountTxLog;
    }
}

以上可以看到 TCC 模式 对业务代码的侵入性比较严重,所以在不是对性能有必须要求的情况下建议AT模式。

建议先写个demo跑起来再搞原理。demo看到结果后,你才会更有兴趣想去发现怎么实现的。

AT模式参考前边,有写过,有问题也请指出

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值