TCC模式演示案例

Tcc模式多用于非关系型数据库,由于非关系型数据库是没有事务这个概念的,所以需要程序员自定义代码。实现三个方法。下面是三个方法。

  • Try:资源的检测和预留

  • Confirm:业务执行和提交

  • Cancel:预留资源释放

补充:

TCC是和AT和TC兼容的。
  1. 需要考虑业务悬挂情况:

对于已经空回滚的业务,之前被阻塞的try操作恢复,继续执行try,就永远不可能confirm或cancel ,事务一直处于中间状态,这就是业务悬挂。解决办法:执行try操作时,应当判断cancel是否已经执行过了,如果已经执行,应当阻止空回滚后的try操作,避免悬挂

  1. 需要考虑幂等性的情况:

微服务因为某种原因失联了,客户端发送n次信息,解决方案:只计算一次

  1. 需要考虑空回滚的情况:

在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。给TC发命令告诉他已经完成了,要不然会一直回滚。

解决办法:执行cancel操作时,应当判断try是否已经执行,如果尚未执行,则应该空回滚。

接下来将开始做示例如何基于mybatis搭建项目,实现重构三种方法实现TCC。

一、项目搭建

1.1例子,建表

1)CREATE TABLE `tb_account_freeze`  (
  `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_id` int(11) NOT NULL,
  `freeze_money` int(11) UNSIGNED NULL DEFAULT 0,
  `state` int(1) NULL DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',  
  PRIMARY KEY (`xid`) USING BTREE
);

1.2创建实体类

@Data
@TableName("tb_account_freeze")//绑定上面的表
public class AccountFreeze {
    @TableId(type = IdType.INPUT)
    private String xid;
    private Integer userId;
    private Integer freezeMoney;
    private Integer state;

    public static abstract class State {
        public final static int TRY = 0;
        public final static int CONFIRM = 1;
        public final static int CANCEL = 2;
    }
}

1.3Mapper接口

public interface AccountFreezeMapper extends BaseMapper<AccountFreeze> {
}

1.4声明TCC接口

@LocalTCC
public interface AccountTCCService {

    /**
     *  一阶段:try 预留
     *  @TwoPhaseBusinessAction 定义 try confirm cancel 方法名
     */
    @TwoPhaseBusinessAction(name = "deduct", commitMethod = "confirm", rollbackMethod = "cancel")//@BusinessActionContextParameter这个注解是把对应的参数存储到类似ThreadLocalt的空间中,供BusinessActionContext注解去获取。
    void deduct(@BusinessActionContextParameter(paramName = "userId") Integer userId,
                @BusinessActionContextParameter(paramName = "money") Integer money);

    /**
     *  二阶段:confirm 提交
     * @param ctx 可以获得 @BusinessActionContextParameter参数和xid
     * 
     */
    Boolean confirm(BusinessActionContext ctx);

    /**
     * 二阶段:cancel 回滚
     * @param ctx 可以获得 @BusinessActionContextParameter参数和xid
     *  
     */
    Boolean cancel(BusinessActionContext ctx);
}

1.5实现TCC接口

@Service
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountFreezeMapper freezeMapper;

    @Override
    public void deduct(Integer userId, Integer money) {
        // 0.获取事务id
        String xid = RootContext.getXID();
        // 解决业务悬挂
        AccountFreeze oldFreeze = freezeMapper.selectById(xid);
        if (oldFreeze != null) {
            return;
        }
        // 1.扣除可用余额
        accountMapper.deduct(userId, money);
        // 2.记录冻结金额,事务状态
        AccountFreeze freeze = new AccountFreeze();
        freeze.setXid(xid);
        freeze.setUserId(userId);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freezeMapper.insert(freeze);

    }

    @Override
    public boolean confirm(BusinessActionContext ctx) {
        // 1.获取xid
        String xid = ctx.getXid();
        // 2.删除冻结记录
        int count = freezeMapper.deleteById(xid);
        return count == 1;
    }

    @Override
    @Transactional
    public boolean cancel(BusinessActionContext ctx) {
        // 1.查询冻结记录
        String xid = ctx.getXid();
        AccountFreeze freeze = freezeMapper.selectById(xid);

        // 解决空回滚
        if (freeze == null) {
            int userId = Integer.parseInt(ctx.getActionContext("userId").toString());
            freeze = new AccountFreeze();
            freeze.setXid(xid);
            freeze.setUserId(userId);
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.CANCEL);
            freezeMapper.insert(freeze);
            return true;
        }

        // 解决幂等性
        if (freeze.getState() == AccountFreeze.State.CANCEL) {
            return true;
        }

        // 2.恢复账户金额
        accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());
        // 3.更新冻结记录
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = freezeMapper.updateById(freeze);
        return count == 1;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值