实战!阿里神器 Seata 实现 TCC模式 解决分布式事务,真香(三)

love374
发布于 2022-7-12 17:05
浏览
0收藏

 

TCC事务模式的落地实现
在前面文章中介绍了Seata的AT模式,有不清楚的可以看:对比7种分布式事务方案,还是偏爱阿里开源的Seata,真香!(原理+实战)

 

当然Seata支持的事务模式不局限于AT模式,还有TCC模式、SAGA模式、XA模式,下面整合一下TCC模式。

 

1、演示场景

就以电商系统中下订单为例,为了演示,直接去掉账户服务,以订单服务、库存服务为例介绍。

 

具体的逻辑如下:

  1. 客户端调用下订单接口
  2. 扣库存
  3. 创建订单
  4. 请求完成


根据上面的逻辑可知,订单服务肯定是主业务服务,事务的发起方,库存服务是从业务服务,参与事务的决策。

 

Seata的AT模式解决方案伪代码如下:

@GlobalTransactional
public Result<Void> createOrder(Long productId,Long num,.....){
    //1、扣库存
    reduceStorage();
    //2、创建订单
    saveOrder();
}

 

@GlobalTransactional这个注解用于发起一个全局事务。

 

但是AT模式有局限性,如下:

  • 性能低,锁定资源时间太长
  • 无法解决跨应用的事务


因此对于要求性能的下单接口,可以考虑使用TCC模式进行拆分成两阶段执行,这样整个流程锁定资源的时间将会变短,性能也能提高。

 

此时的TCC模式的拆分如下:

 

1、一阶段的Try操作

TCC模式中的Try阶段其实就是预留资源,在这个过程中可以将需要的商品数量的库存冻结,这样就要在库存表中维护一个冻结的库存这个字段。

 

伪代码如下:

@Transactional
public boolean try(){
  //冻结库存
  frozenStorage();
  //生成订单,状态为待确认
  saveOrder();
}

“注意:@Transactional开启了本地事务,只要出现了异常,本地事务将会回滚,同时执行第二阶段的cancel操作。”


2、二阶段的confirm操作

confirm操作在一阶段try操作成功之后提交事务,涉及到的操作如下:

  1. 释放try操作冻结的库存(冻结库存-购买数量)
  2. 生成订单


伪代码如下:

@Transactional
public boolean confirm(){
    //释放掉try操作预留的库存
    cleanFrozen();
    //修改订单,状态为已完成
    updateOrder();
    return true;
}

“注意:这里如果返回false,遵循TCC规范,应该要不断重试,直到confirm完成。”


3、二阶段的cancel操作

cancel操作在一阶段try操作出现异常之后执行,用于回滚资源,涉及到的操作如下:

  • 恢复冻结的库存(冻结库存-购买数量、库存+购买数量)
  • 删除订单


伪代码如下:

@Transactional
public boolean cancel(){
    //释放掉try操作预留的库存
    rollbackFrozen();
    //修改订单,状态为已完成
    delOrder();
    return true;
}

“注意:这里如果返回false,遵循TCC规范,应该要不断重试,直到cancel完成。”


2、TCC事务模型的三个异常
实现TCC事务模型涉及到的三个异常是不可避免的,实际生产中必须要规避这三大异常。

 

1、空回滚

定义:在未调用try方法或try方法未执行成功的情况下,就执行了cancel方法进行了回滚。

 

怎么理解呢?未调用try方法就执行了cancel方法,这个很容易理解,既然没有预留资源,那么肯定是不能回滚。

 

try方法未执行成功是什么意思?

 

可以看上节中的第一阶段try方法的伪代码,由于try方法开启了本地事务,一旦try方法执行过程中出现了异常,将会导致try方法的本地事务回滚(注意这里不是cancel方法回滚,而是try方法的本地事务回滚),这样其实try方法中的所有操作都将会回滚,也就没有必要调用cancel方法。

 

但是实际上一旦try方法抛出了异常,那么必定是要调用cancel方法进行回滚,这样就导致了空回滚。

 

解决方案:

 

解决逻辑很简单:在cancel方法执行操作之前,必须要知道try方法是否执行成功。

 

2、幂等性

TCC模式定义中提到:如果confirm或者cancel方法执行失败,要一直重试直到成功。

 

这里就涉及了幂等性,confirm和cancel方法必须保证同一个全局事务中的幂等性。

 

解决方案:

 

解决逻辑很简单:对付幂等,自然是要利用幂等标识进行防重操作。

 

文章转自公众号:码猿技术专栏

标签
已于2022-7-12 17:05:24修改
收藏
回复
举报
回复
    相关推荐