首页 > 编程知识 正文

分布式事务解决方案seata,springcloud分布式事务解决方案

时间:2023-05-04 09:36:27 阅读:48885 作者:4818

简介TCC是Try、Confirm、Cancel三个词的缩写,TCC要求每个分支事务进行Try预处理、Confirm确认和Cancel撤销三个操作。 Try操作进行业务检查和资源确保,Confirm进行业务确认操作,Cancel实现与Try或Commit相反的操作——回滚操作。 TM首先启动所有分支事务处理的try操作,如果其中一个分支事务处理的try操作失败,则TM启动所有分支事务处理的Cancel操作;如果try操作全部成功,则TM启动所有分支事务处理的Confirm操作其中,如果确认/取消操作失败,TM将重试。

业务场景先看看业务场景吧。 假设你现在有电子商务系统,其中有支付订单的场景。

它对一个订单支付后,我们需要进行下一步。

将订单状态更改为“已支付”,扣除商品库存,向会员添加积分,制作销售发票,通知仓库有发货业务场景。 现在,再进一步,实现TCC分布式事务的效果。

什么意思? 也就是说:

订单服务-修改订单状态的库存服务-扣除库存点服务-添加积分仓库服务-创建销售发票。 这些步骤必须是一起成功,一起失败,或者是整体事务。

举个例子,现在订单的状态更改为“已支付”,库存服务扣除库存失败。 那个商品的库存本来是100件,现在卖了2件,本来应该是98件了。

结果呢? 由于库存服务运营数据库异常,库存数量仍为100。 这不是在陷害人吗,当然不能发生这种情况!

但是,如果不使用TCC分布式事务方式,很可能在go上开发这样的微服务系统来做这种事。

请看下图。 直观地表达了上述过程。

这意味着,必须使用TCC分布式事务机制来确保每个服务形成一个整体事务。

上述几个步骤都成功,或者如果其中一个服务操作失败,则全部回滚并取消完成的操作。

例如,如果库存服务的库存扣减失败,订单服务必须取消修改订单状态的操作,然后停止两个操作:增加积分和通知发放。

要实现落地TCC分布式事务,请以python开发系统为背景进行说明。

TCC实施阶段1:try首先,在订单服务中,其代码应该大致是这样的。

classorderservice 3360 def _ init _ (self,inv_srv,credit_srv, wms_srv ) :self.inv_srv=inv_srv #库存服务self.credit_srv=credit_srv #积分服务self.WMS _ SRV=wmms status ) 3360passdefnotify(self ) : self.update _ order _ status (trade _ sucess ' ) self.inv_SRV.reduce点实际上,订单服务完成本地数据库操作后,只需通过grpc调用每个其他服务。

但是,光靠这段代码还不足以实现TCC分布式事务,对吧? 各位,请不要着急。 我们可以修改这个订单服务的代码吗?

首先,上面的订单服务首先将自己的状态修改为TRADE_SUCCESS。

这是什么意思? 也就是说,pay ) )的方法中,请不要将订单状态修改为已直接支付。 请先将订单状态修改为UPDATING,即修改中的含义。

这种状态是没有任何意义的这种状态,表示有人在修正这种状态。

然后,请不要在库存服务直接提供的reduce_stock )界面中直接扣减库存。 你可以冻结库存。

举个例子,本来你的库存量是100。 请扣除这个库存,而不是直接100 - 2=98。

可销售库存: 100 - 2=98设定为98也没关系。 然后,将另一个冻结库存的字段设置为2。 也就是说,有两个库存被冻结了。

积分服务的add_credit (界面也一样,不要直接向用户增加会员积分。 可以首先将积分添加到积分表的预添加点字段中。

例如,用户积分本来是1190,现在增加10个百分点,所以不要就这样定为1190 10=1200个百分点。

如果将点保留为1190,并将预先添加的字段(例如prepare_add_credit字段)设置为10,则表示已准备添加10个点。

仓库服务的sale_delivery (接口也一样啊。 你可以先制作销售发票,但是这个销售发票的状态是“UNKNOWN”。

也就是说,刚刚制作了这张销售发票,此时不知道状态是什么。

上述接口改造过程实际上是所谓TCC分布式事务的第一个t形表示的阶段,而且

就是 Try 阶段。

总结上述过程,如果你要实现一个 TCC 分布式事务,首先你的业务的主流程以及各个接口提供的业务含义,不是说直接完成那个业务操作,而是完成一个 Try 的操作。

这个操作,一般都是锁定某个资源,设置一个预备类的状态,冻结部分数据,等等,大概都是这类操作。

咱们来一起看看下面这张图,结合上面的文字,再来捋一捋整个过程:

TCC 实现阶段二:Confirm

然后就分成两种情况了,第一种情况是比较理想的,那就是各个服务执行自己的那个 Try 操作,都执行成功了,Bingo!

这个时候,就需要依靠 TCC 分布式事务框架来推动后续的执行了。这里简单提一句,如果你要玩儿 TCC 分布式事务,必须引入一款 TCC 分布式事务框架,比如java国内开源的 seata、ByteTCC、Himly、TCC-transaction。

否则的话,感知各个阶段的执行情况以及推进执行下一个阶段的这些事情,不太可能自己手写实现,太复杂了。

如果你在各个服务里引入了一个 TCC 分布式事务的框架,订单服务里内嵌的那个 TCC 分布式事务框架可以感知到,各个服务的 Try 操作都成功了。

此时,TCC 分布式事务框架会控制进入 TCC 下一个阶段,第一个 C 阶段,也就是 Confirm 阶段。

为了实现这个阶段,你需要在各个服务里再加入一些代码。比如说,订单服务里,你可以加入一个 Confirm 的逻辑,就是正式把订单的状态设置为“已支付”了,大概是类似下面这样子:

库存服务也是类似的,你可以有一个 InventoryServiceConfirm 类,里面提供一个 reduce_stock() 接口的 Confirm 逻辑,这里就是将之前冻结库存字段的 2 个库存扣掉变为 0。

这样的话,可销售库存之前就已经变为 98 了,现在冻结的 2 个库存也没了,那就正式完成了库存的扣减。

积分服务也是类似的,可以在积分服务里提供一个 CreditServiceConfirm 类,里面有一个 addCredit() 接口的 Confirm 逻辑,就是将预增加字段的 10 个积分扣掉,然后加入实际的会员积分字段中,从 1190 变为 1120。

仓储服务也是类似,可以在仓储服务中提供一个 WmsServiceConfirm 类,提供一个 sale_delivery() 接口的 Confirm 逻辑,将销售出库单的状态正式修改为“已创建”,可以供仓储管理人员查看和使用,而不是停留在之前的中间状态“UNKNOWN”了。

好了,上面各种服务的 Confirm 的逻辑都实现好了,一旦订单服务里面的 TCC 分布式事务框架感知到各个服务的 Try 阶段都成功了以后,就会执行各个服务的 Confirm 逻辑。

订单服务内的 TCC 事务框架会负责跟其他各个服务内的 TCC 事务框架进行通信,依次调用各个服务的 Confirm 逻辑。然后,正式完成各个服务的所有业务逻辑的执行。

同样,给大家来一张图,顺着图一起来看看整个过程:

TCC 实现阶段三:Cancel

好,这是比较正常的一种情况,那如果是异常的一种情况呢?

举个例子:在 Try 阶段,比如积分服务吧,它执行出错了,此时会怎么样?

那订单服务内的 TCC 事务框架是可以感知到的,然后它会决定对整个 TCC 分布式事务进行回滚。

也就是说,会执行各个服务的第二个 C 阶段,Cancel 阶段。同样,为了实现这个 Cancel 阶段,各个服务还得加一些代码。

首先订单服务,它得提供一个 OrderServiceCancel 的类,朴素的河马有一个 pay() 接口的 Cancel 逻辑,就是可以将订单的状态设置为“CANCELED”,也就是这个订单的状态是已取消。

库存服务也是同理,可以提供 reduce_stock() 的 Cancel 逻辑,就是将冻结库存扣减掉 2,加回到可销售库存里去,98 + 2 = 100。

积分服务也需要提供 addCredit() 接口的 Cancel 逻辑,将预增加积分字段的 10 个积分扣减掉。

仓储服务也需要提供一个 sale_delivery() 接口的 Cancel 逻辑,将销售出库单的状态修改为“CANCELED”设置为已取消。

然后这个时候,订单服务的 TCC 分布式事务框架只要感知到了任何一个服务的 Try 逻辑失败了,就会跟各个服务内的 TCC 分布式事务框架进行通信,然后调用各个服务的 Cancel 逻辑。

大家看看下面的图,直观的感受一下:

总结与思考

总结一下,你要玩儿TCC分布式事务的话:

首先需要选择某种TCC分布式事务框架,各个服务里就会有这个TCC分布式事务框架在运行。然后你原本的一个接口,要改造为3个逻辑,Try-Confirm-Cancel。 先是服务调用链路依次执行Try逻辑如果都正常的话,TCC分布式事务框架推进执行Confirm逻辑,完成整个事务如果某个服务的Try逻辑有问题,TCC分布式事务框架感知到之后就会推进执行各个服务的Cancel逻辑,撤销之前执行的各种操作。这就是所谓的TCC分布式事务。TCC分布式事务的核心思想,说白了,就是当遇到下面这些情况时, 某个服务的数据库宕机了某个服务自己挂了那个服务的redis、elasticsearch、MQ等基础设施故障了某些资源不足了,比如说库存不够这些 先来Try一下,不要把业务逻辑完成,先试试看,看各个服务能不能基本正常运转,能不能先冻结我需要的资源。如果Try都ok,也就是说,底层的数据库、redis、elasticsearch、MQ都是可以写入数据的,并且你保留好了需要使用的一些资源(比如冻结了一部分库存)。接着,再执行各个服务的Confirm逻辑,基本上Confirm就可以很大概率保证一个分布式事务的完成了。那如果Try阶段某个服务就失败了,比如说底层的数据库挂了,或者redis挂了,等等。此时就自动执行各个服务的Cancel逻辑,把之前的Try逻辑都回滚,所有服务都不要执行任何设计的业务逻辑。保证大家要么一起成功,要么一起失败。 终极大招 如果有一些意外的情况发生了,比如说订单服务突然挂了,然后再次重启,TCC分布式事务框架是如何保证之前没执行完的分布式事务继续执行的呢?TCC事务框架都是要记录一些分布式事务的活动日志的,可以在磁盘上的日志文件里记录,也可以在数据库里记录。保存下来分布式事务运行的各个阶段和状态。万一某个服务的Cancel或者Confirm逻辑执行一直失败怎么办呢?那也很简单,TCC事务框架会通过活动日志记录各个服务的状态。举个例子,比如发现某个服务的Cancel或者Confirm一直没成功,会不停的重试调用他的Cancel或者Confirm逻辑,务必要他成功!当然了,如果你的代码没有写什么bug,有充足的测试,而且Try阶段都基本尝试了一下,那么其实一般Confirm、Cancel都是可以成功的!如果实在解决不了,那么这个一定是很小概率的事件,这个时候发邮件通知人工处理
seata、 go-seata TCC优缺点

优点:
1.解决了跨服务的业务操作原子性问题,例如组合支付,订单减库存等场景非常实用

2.TCC的本质原理是把数据库的二阶段提交上升到微服务来实现,从而避免了数据库2阶段中锁冲突的长事务低性能风险。

3.TCC异步高性能,它采用了try先检查,然后异步实现confirm,真正提交的是在confirm方法中。

缺点:
1.对微服务的侵入性强,微服务的每个事务都必须实现try,confirm,cancel等3个方法,开发成本高,今后维护改造的成本也高。

2.为了达到事务的一致性要求,try,confirm、cancel接口必须实现等幂性操作。

(定时器+重试)

3.由于事务管理器要记录事务日志,必定会损耗一定的性能,并使得整个TCC事务时间拉长,建议采用redis的方式来记录事务日志。

tcc需要通过锁来确保数据的一致性,会加锁导致性能不高

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。