Go微服务: 关于TCC分布式事务

TCC 分布式事务

  • T: Try 预处理, 尝试执行,完成所有的业务检查,做好一致性,预留必要的业务资源,做好准隔离性
  • C: Confirm 确认,如果所有的分支Try都成功了, 就到了这个阶段, Confirm 是真正执行业务的过程, 不做任何业务检查, 只使用 try 阶段预留的业务资源
  • C: Cancel 取消,所有的分支中,如果有一个失败了,则走到 Cancel 的阶段,Cancel 就释放了 Try 阶段预留的业务资源
  • 所以,Confirm 和 Cancel 是互斥的,只会执行一个,按照TCC的协议,Confirm 和 Cancel 只返回成功,不返回失败
  • 如果有网络问题,或服务器临时故障, 比如崩溃,掉电这种, 事务管理器会进行重试,最终让他成功
  • 在 Try 阶段要做业务检查,保障一致性以及资源预留隔离的一些问题
  • 此阶段只是一个初步的操作, 它和后续的Confirm一起才能真正构成完整的业务逻辑, 这个算是一部分,当所有的 Try 都完成了,就会向下执行,到Confirm
  • Confirm 阶段做一个确认提交, Try 阶段的所有分支事务执行成功后, 开始执行 Confirm, 采用TCC时候,只要Try 成功了,就表示Confirm 阶段不会出错,如果 Confirm 阶段出错,就引入重试机制, 或者人工处理
  • 上图是在 Try 阶段, 资源2执行失败,资源1执行成功, 要回滚资源1,这时候资源1要回滚,就是执行 Cancel, 业务执行错误,需要回滚,执行分支的事务的业务取消,预留资源的释放,一般认为Cancel阶段也是一定成功的, 如果Cancel 出错,也是重试机制和人工处理
  • 上面全局事务发起方是全局事务的管理器,是独立的第三方,可以实现独立的服务,会生成全局的事务记录,全局的事务id贯穿整个分布式事务的调用链,类似 jaeger 的链路追踪traceID,都是分布式的产品,这些产品设计思路都是相互借鉴,所以,这个全局事务管理器可以追踪和调用状态,由于 Confirm 和 Cancel 失败需要进行重试, 所以要实现幂等性,也就是说同一个操作,无论请求多少次,结果都应该是相同的
  • TCC 的三种异常处理:空回滚,幂等,悬挂
    • 1 )空回滚:在没有调用 Try 阶段的方法而调用了第二阶段的 Cancel 方法
    • Cancel 方法要识别出这是一个空回滚, 然后直接返回成功
    • 也就是说空回滚在某种情况下,没有执行 Try, 就直接回滚了
    • 2 )幂等:为了保证TCC的二阶段提交重试机制不会引发数据不一致
    • 就要求TCC的二阶段Try, Confirm, Cancel 接口保证幂等,这样不会重复使用或者释放资源,如果幂等没有控制做好,就很容易导致数据不一致的严重问题
    • 解决思路是增加执行状态,每次执行前,就要查询状态,这就是说你能不能证明你的状态执行到哪里了,可以做一个记录
    • 3 )悬挂:对于一个分布式事务,二阶段的Cancel接口比Try接口先执行了
    • 悬挂对于分布式的二阶段,Cancel 接口比Try接口先执行
    • 对比空回滚是Try是不执行;而悬挂是Cancel先执行,Try再执行
    • 它的解决思路是,如果二阶段执行成功,一阶段就不能再继续执行了,在执行一阶段事务时要判断一下,全局事务是否已经有二阶段事务的记录,如果有,就不执行Try了

TCC 为何这么难?


1 ) 从代码上来说

  • 可以把我们的TCC放到我们的代码,或者说放到一些场景里来看一下
  • 好,我们先从代码说起, 那我们在这里先新建一个目录,就叫 TCC Demo
  • 按照TCC的规则来,我们每一个方法函数都需要有这三个函数: Try, Confirm, Cancel
  • 比如订单业务,有三个函数
    • func TryOrder() {}
    • func ConfirmOrder() {}
    • func CancelOrder() {}
  • 比如下单的时候,需要协同构建库存,增加积分
  • 对于库存来说, 有:
    • func TryStock(){}
    • func ConfirmStock(){}
    • func CancelStock(){}
  • 对于积分来说,有:
    • func TryPoint(){}
    • func ConfirmStock(){}
    • func CancelOrder(){}
  • 如果我们业务中有10个这种,那么我们要写30个这类服务,而且需要有数据库的配合,非常的繁琐

2 ) 基于业务场景来看

  • 先看左边的订单服务,它有自己的这个mysql数据库。它第一步先启动事物和这个TCC事务管理器去交互
  • 它启动事物之后,第二步就调用这个Try,调用所有的资源上的Try
  • 第三步就是要执行提交或者回滚,现在,我们的Try都是ok的
  • 下面它就要执行第四步,这第四步的这个Confirm和Cancel是互斥的, 只能择其一执行
  • 因为你这个Try执行成功了,就直接都是 confirmStock 和 confirmPoint
  • 如果要是有问题,中间可能就是 CancelStock 和 CancelPoint
    在实际开发中一般是一个什么样的情况呢?
    • 就是说 Try 一般是执行主业务的, 比如我们有一个非常难的业务, 在try里基本都搞定了
    • 那对于这个 Confirm和Cancel, 它一般都是基本是比较简单的操作
    • 其实一般只要Try里没有问题, 那么Confirm和这个Cancel就基本上可以认为是没有太大问题,但是还有其他的问题
    • 并不是说,Try ok了,然后 Confirm OK了,Cancel 也 ok 了,就没有问题
    • 分布式的复杂,它是存在各种问题的,比如说你这个分布式网络,你永远不要相信分布式里网络永远是好的
    • 或者说我现在这个库存有另一个小组维护,这个库存它没有问题,但是我们这个积分服务挂了,或者说很慢
    • 我们说这个库存服务,还有这个积分服务,都是其他小组维护的,我们站在我们这个订单角度来说,比如说我是订单服务的负责人
    • 那我不能说动人家这个积分服务的这个代码,比如说积分服务,是挂了或者处理很慢
    • 就要去看这个TCC的事务管理器了,其实这个TCC在开启事务之前,它会详细的记录日志
    • 它有一个日志,每个服务调用了哪个接口,当前事务执行到哪里?
    • 比如,即便积分服务挂了,重启了,对吧?重启以后,我们找到日志,找到这个日志仍然可以继续执行
    • 假如,我们的积分服务 Cancel 执行这个出错了, 那么我们的TCC事务会一直重试
    • 那么TCC事务管理器,要保证执行Cancel不出错,它就会重试。
    • 这样一来就可以解决大部分的网络带来的这个问题
    • 因为Cancel, Confirm, 这里面的这个业务逻辑基本不是很复杂
    • 可能改个状态,或者是添加个什么东西,就是这种类型的,它可能就是网络抖动, 给你造成了一下这个小麻烦
    • 那你你这个重试几次之后呢,补偿一下就可以
    • 从这儿可以看到TCC事务管理器控制整个流程
  • 我们调用TCC,如果Try阶段有问题,那TCC框架就会执行服务的Cancel逻辑撤销之前的操作
  • 如果,我们调用这个TCC如果Try阶段它就有问题,比如说你的这个业务太复杂了,在某个地方突然出了一个小bug
  • 那么TCC框架就会执行各种Cancel逻辑撤销之前的操作
  • 如果上图这个Confirm或Cancel它就是一直不成功,怎么办?
  • 其实分布式它本身虽然比较复杂,但是大部分业务的Confirm和Cancel代码就是很简单
  • 一般的都是网络问题,通过一直调用的进行补偿就可以了
  • 如果一直不成功,你可以在某种程度上做一个记录器进行报警,人为干预
  • 自己做的计录器可以按你自己的业务逻辑,因为你这个TCC事务,比如三次或五次都不成功,它要保证成功,则可以通过电话,微信,钉钉通知人报警
  • 这时候,比如说开发人员肯定是收到了嘛,人为介入解决这个问题就行了嘛
  • 所以,其实TCC真的是不简单,我们只要使用了微服务和这个分布式,就没有办法避免没有这些问题,只要你用了就会遇到这些问题

相关推荐

  1. 服务架构下的分布式事务

    2024-06-08 00:26:03       49 阅读
  2. SpringClould服务+分布式事务笔记

    2024-06-08 00:26:03       50 阅读

最近更新

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

    2024-06-08 00:26:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-08 00:26:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-06-08 00:26:03       82 阅读
  4. Python语言-面向对象

    2024-06-08 00:26:03       91 阅读

热门阅读

  1. Log4j日志级别介绍

    2024-06-08 00:26:03       27 阅读
  2. 数据结构:哈夫曼树及其哈夫曼编码

    2024-06-08 00:26:03       27 阅读
  3. 区块链技术的应用场景和优势

    2024-06-08 00:26:03       34 阅读
  4. 九天毕昇深度学习平台 | TensorBoard使用

    2024-06-08 00:26:03       31 阅读
  5. Python 树状数组

    2024-06-08 00:26:03       28 阅读
  6. Mybatis配置

    2024-06-08 00:26:03       29 阅读