06_TCC分布式事务

一. 常用的TCC技术方案

1.1 通用型TCC技术方案

适用于大多数场景,主要由以下三个组件构成:

  1. 主业务服务
    它调用了其它从业务服务的方法,有着分布式事务的需求,负责整个分布式事务的编排、管理、执行以及回滚操作。
  2. 从业务服务
    对外提供接口,实现自己的业务功能,供主业务服务调用。从业务服务至少要实现三个接口:try接口、confirm接口、cancel接口。
  3. 业务活动管理器
    负责记录分布式事务中,各个从业务服务的事务状态,以及向从业务服务发起confirm、cancel等调用请求。

通用型的TCC技术方案包含了三个环节:

  • try阶段
    查看目标服务是否有能力执行本次操作,锁定待操作的资源,或者预分配资源。
  • confirm阶段
    执行具体的业务逻辑。
  • cancel阶段
    如果try阶段或者confirm阶段中,任意一个子业务服务执行有问题,则需要通过cancel阶段,进行事务回滚,还原之前对资源做的操作。
    不同的TCC技术实现方案,对于cancel阶段的处理是有差别的。比如说ByteTCC,它只会对try阶段进行回滚,根本就不会对confirm阶段执行回滚。如果遇到try阶段全部执行成功,但某些从业务服务的confirm阶段执行失败,那么ByteTCC会不断的向这些从业务服务发送confirm请求,直到收到请求成功的响应结果。
    在这里插入图片描述
    上图中有一个非常重要的组件,那就是“业务活动管理器”,它会记录整个分布式事务执行过程中所有从业务服务的活动状态,有了这些记录,业务活动管理器就知道,在什么时候需要调用confirm接口,什么时候需要调用cancel接口。

TCC分布式事务的执行流程如下:

  1. 主业务服务开启本地事务,执行自身的业务操作,并存储到自己的数据库中,但不提交。
  2. 主业务服务请求业务活动管理器启动一个分布式事务,让业务活动管理器注册本次分布式事务中涉及到的所有从业务服务,对应到图上的“从业务活动”方框。
  3. 主业务服务向本次涉及的从业务服务发起请求,请求try接口。
  4. 如果所有的从业务服务try接口全部按时返回结果,并执行成功,则主业务服务提交本地事务,然后通知业务活动管理器调用各个从业务服务的confirm接口。
  5. 如果任何一个从业务服务try接口有问题,则主业务服务回滚本地事务,然后通知业务活动管理器调用各个从业务服务的cancel接口。
  6. 如果任何一个从业务服务confirm接口有问题,则业务活动管理器也会调用各个从业务服务的cancel接口。
  7. 分布式事务结束。

上述是老师讲的内容,有一个槽点。

为什么不把主业务服务交给业务活动管理器统一管理?上方的第6步显然是有问题的,假如任何一个从业务服务confirm接口有问题,此时的确能依靠业务活动管理器调用从业务服务的cancel接口进行回滚,但是主业务服务怎么办?从第4步中,我们知道,当try成功后,主业务服务的本地事务已经提交了,这还让别人怎么回滚?为什么不能在所有子业务服务的事务全部提交完毕后,最后提交主业务服务的事务呢?

1.2 异步确保型TCC技术方案

通过前面学习通用型TCC技术方案,我们知道,如果想把自己的分布式系统接入TCC,那么每个从业务服务就必须要多提供2个接口(try和cancel),增加工作量。

那么有没有简便的方案呢?答案是有的,我们可以在主业务服务和从业务服务之间,加一个可靠消息服务。主业务服务不再直接与从业务服务交互了,而是与这个可靠消息服务交互。此外,业务活动管理器也不需要了。

可靠消息服务会在try阶段插入一条消息到自己的数据库(记录本次需要执行的操作),在接收到confirm请求后,可靠消息服务参照着try阶段在数据库内记录的数据,调用从业务服务实际的业务接口。如果调用失败,那么只需要由可靠消息服务删除自己的本地消息即可。

上述是老师讲的内容,感觉槽点很多啊。

(1) 假如在confirm阶段,有些从业务服务调用成功,有些从业务服务调用失败,此时老师给出的方案居然是让可靠消息服务删除自己的本地消息,这不是搞笑吗?从业务服务都有着自己的数据库,你删除可靠消息服务关联的数据库中的数据有什么用?难不成还能让从业务服务提交的事务回滚?是可以这么做的,一般我们会保证从业务服务的接口包裹在一个本地事务内,如果可靠消息服务请求失败,则标志着从业务服务出现了故障,这个时候可以通过本地事务来回滚,而可靠消息服务只需要删除自己的本地消息即可。

(2) 这个try阶段也很奇怪,让可靠消息服务往本地数据库中插入一条消息,记录本次操作,这就叫try阶段了吗?try的核心意义在于检查子业务服务是否拥有在接下来执行业务操作的能力吧。 怎么说呢,学完ByteTCC后,我发现try阶段所做的操作不是固定的,因为实际的业务场景不同,try阶段的实现逻辑千差万别。有的场景会把具体的业务逻辑放到try阶段,但是在有些场景中,try阶段只需要往数据库中插入一条数据, 后续的confirm阶段依据这条数据,判断是否需要执行confirm。如果confirm执行失败,有其自身的本地事务帮忙回滚,cancel操作也只需要删掉这条try阶段产生的消息即可。

1.3 补偿性TCC解决方案

只需要从业务逻辑提供两个接口:Do和Compensate,舍弃try阶段。Do就类似之前的confirm阶段,Compensate类似之前的cancel阶段。

优点是比通用的TCC解决方案少写一个接口,缺点有两点:

  1. 没有做try,直接调用Do,相较之前更容易报错,因为没有提前检查当前的子业务服务是否拥有执行接下来的业务操作的能力。(想着大不了调用一下Compensate补偿接口,所以有恃无恐)
  2. 由于没做try,导致资源没锁定,所以子业务服务报错后,在执行补偿机制时,也可能报错。

二. 实现TCC方案需要注意的问题

  • 空回滚
    比如try阶段,因为网络原因,主业务服务没有调通从业务服务的try接口,误以为try接口执行失败,导致调用了从业务服务的cancel接口,这个时候就需要从业务服务自己来判断,是否需要什么事情都不做,也就是空回滚了。

  • try回滚和confirm回滚
    try阶段中,只要任意一个服务的try执行失败了,那么必须调用所有try阶段执行成功的服务的cancel来回滚try操作,confirm阶段也是同理。

  • 倒置请求
    比如主业务服务调用从业务服务的try接口,因为网络原因,误以为try执行失败,结果执行cancel,哪知道过了一段时间后,这个try请求又被发过来了。此时,我们就不能去执行这个try了。同样的道理,比如confirm阶段超时了,结果都cancel掉了,但是过了几秒后,confirm请求又被发过来了,此时就不能执行confirm了。

  • 接口的幂等性保证
    无论是try、confirm还是cancel,都有可能被大量重复的调用,这个时候,接口的幂等性必须得到保证。

上述问题,不同的TCC实现技术的实现差距较大,比如对于ByteTCC来说,只有try阶段出错才会触发cancel去回滚,对于confirm和cancel阶段而言,如果请求调用报错,则会不断的请求重试,直到从业务服务收到请求、执行请求,并正确的响应请求为止。

三. 常见的TCC分布式事务解决方案

有三个,分别是tcc-transaction、himly、ByteTCC。其中,tcc-transaction对Spring Cloud的支持不好,himly大量基于xml配置,没有通过注解驱动,而ByteTCC是比较适合的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值