三十九、TCC模式

目录

一、定义

1、需要实现的方法:

2、优点:

3、缺点:

二、原理

1、例子:

2、工作模型图:

3、空回滚和业务悬挂

三、实现TCC模式

1、编写TCC服务接口

2、实现TCC服务接口


一、定义

  • TCC模式是Translucent Transmission Control(半透明传输控制)的简称,是一种网络传输协议。
  • 该协议能够识别网络拥塞,并在拥塞时降低传输速率,以防止丢包和网络拥塞。
  • TCC模式通过在传输过程中监测延迟、丢包和网络拥塞来自动调整传输速率。
  • 该协议适用于多媒体流、实时视频、在线游戏等需要低延迟和高带宽的应用。

1、需要实现的方法:

  • Try:资源的检测和预留;
  • Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功
  • Cancel:预留资源释放,可以理解为try的反向操作

2、优点:

  • 一阶段完成直接提交事务,释放数据库资源,性能好
  • 相比AT模型,无需生成快照,无需使用全局锁,性能最强
  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

3、缺点:

  • 有代码侵入,需要人为编写tryConfirmCancel接口,太麻烦
  • 软状态,事务是最终一致
  • 需要考虑ConfirmCancel的失败情况,做好幂等处理

二、原理

1、例子:

2、工作模型图:

3、空回滚和业务悬挂

  1. 空回滚

在分布式事务中,一个事务涉及到多个资源,如果其中一个资源在分布式事务提交后因为各种原因(如网络故障、硬件故障等)未提交修改,那么整个分布式事务将无法提交,需要进行回滚操作。但是,如果其他资源已经提交修改,并且无法回滚,则存在空回滚的情况。

空回滚指的是分布式事务中已经提交了修改,但是由于某些原因无法回滚的情况,导致整个事务无法提交,只能回滚,造成了资源的浪费和性能的降低。

  1. 业务悬挂

业务悬挂指的是当一个分布式事务在执行过程中,一个或多个资源长时间被锁定,导致其他分布式事务无法访问该资源,进而产生阻塞和死锁的问题。

例如,在一个分布式事务中,涉及到资源1和资源2。当该事务对资源1进行锁定并执行修改时,在此期间,如果其他事务也需要对资源1进行访问和修改,就需要等待该事务完成;而当该事务需要对资源2进行访问和修改时,如果此时资源2也被其他事务锁定,就会出现阻塞和死锁的情况。

为了避免业务悬挂的问题,分布式事务协调器通常会采用分布式锁机制,通过对资源的锁定和释放来保证分布式事务的并发执行和数据一致性。

三、实现TCC模式

1、编写TCC服务接口

@LocalTCC
public interface AccountTCCService {
    /**
     * Try逻辑,@TwoPhaseBusinessAction中的name属性要与当前方法名一致,用于指定Try逻辑对应的方法
     */
    @TwoPhaseBusinessAction(name = "deduct", commitMethod = "confirm", rollbackMethod = "cancel")
    void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,
                @BusinessActionContextParameter(paramName = "money") int money);

    /**
     * 二阶段confirm确认方法、可以另命名,但要保证与commitMethod一致       *      * @param context 上下文,可以传递try方法的参数      * @return boolean 执行是否成功
     */
    boolean confirm(BusinessActionContext context);

    /**
     * 二阶段回滚方法,要保证与rollbackMethod一致
     */
    boolean cancel(BusinessActionContext context);
}

2、实现TCC服务接口

@Slf4j
@Service
public class AccountTCCServiceImpl implements AccountTCCService {
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper accountFreezeMapper;

    @Override
    @Transactional
    public void deduct(String userId, int money) {
        //获取事务id
        String xid = RootContext.getXID();
        //判断freeze中是否有冻结记录,如果有,一定是CANCEL执行过,我要拒绝业务(业务悬挂)
        AccountFreeze oldFreeze = accountFreezeMapper.selectById(userId);
        if (oldFreeze != null) {
            return;
        }
        //扣减可用金额
        accountMapper.deduct(userId, money);
        //记录冻结金额,事务状态
        AccountFreeze freeze = new AccountFreeze();
        freeze.setUserId(userId);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freeze.setXid(xid);
        accountFreezeMapper.insert(freeze);
    }

    @Override
    public boolean confirm(BusinessActionContext ctx){
        //查询冻结记录
        String xid = ctx.getXid();
        //根据id删除冻结记录
        int count = accountFreezeMapper.deleteById(xid);
        return count == 1;
    }

    @Override
    public boolean cancel(BusinessActionContext ctx){
        //查询冻结记录
        String xid = ctx.getXid();
        String userId = ctx.getActionContext("userId").toString();
        AccountFreeze freeze = accountFreezeMapper.selectById(xid);
        //空回滚判断
        if (freeze == null) {
            //try未执行,要空回滚
            freeze = new AccountFreeze();
            freeze.setUserId(userId);
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.CANCEL);
            freeze.setXid(xid);
            accountFreezeMapper.insert(freeze);
            return true;
        }
        //幂等判断
        if (freeze.getState() == AccountFreeze.State.CANCEL){
            return true;
        }


        //恢复可用金额
        accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());
        //将冻结金额清零,状态改为CANCEl
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = accountFreezeMapper.updateById(freeze);
        return count == 1;
    }
}

相关推荐

  1. 、Rust Tcp Rpc 示例

    2023-12-07 06:40:02       39 阅读
  2. os模块篇(

    2023-12-07 06:40:02       25 阅读
  3. 第二百

    2023-12-07 06:40:02       62 阅读
  4. web学习笔记(

    2023-12-07 06:40:02       44 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-07 06:40:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-07 06:40:02       101 阅读
  3. 在Django里面运行非项目文件

    2023-12-07 06:40:02       82 阅读
  4. Python语言-面向对象

    2023-12-07 06:40:02       91 阅读

热门阅读

  1. 笔记68:Pytorch中repeat函数的用法

    2023-12-07 06:40:02       67 阅读
  2. 多个项目复用node_modules

    2023-12-07 06:40:02       55 阅读
  3. Tomcat的缺省端口是多少,怎么修改?

    2023-12-07 06:40:02       58 阅读
  4. svn服务端安装

    2023-12-07 06:40:02       59 阅读