从LCN的两阶段提交到TCC补偿事务方案

在前一篇文章 spring cloud 101 中,我们给出了一个学习资源,教程路径中,有一篇就是 LCN 分布式事务框架的整合。感兴趣的朋友可以去试试看,直观的了解下分布式事务框架的能力。

LCN 框架资有b站视频(讲的很不错),有技术原理,适合想了解这块的同学入门。github地址

LCN框架提供了三种分布式事务的支持模式,分别是 LCN 模式,TCC模式和TXC模式。我们就从 LCN 模式开始学习,然后过渡到 TCC 模式。

LCN 的原理文档是比较清晰的,建议去看一下。在这里,只是针对核心点作说明,以保证文章完整性。学习 LCN 的目的,就是对分布式事务的实现有更进一步的认知。

编码是微观层面的架构设计

怎么理解呢?

前面的文章我们谈论过 K8S 等,云原生架构(后面补上),还有服务网格。这些出现,都是让我们的关注点分离,进而聚焦于业务。

边车模式大家应该听说过,没听说过的查阅资料,简单的概念。实际上就是代理,代理了节点上的网络流量,让节点程序只关注于业务,而和业务无关的,比如调度、监控、安全等,全部由代理来解决。这也是服务网格的实现基本思想。

回过头看我们的编程,很多场景下都会使用代理模式,其实 AOP 切面也很像代理模式。核心都是让业务聚焦。

那么 LCN 框架的 lcn 模式就是这样,通过代理等,让我们开发变得非常简单,代码无入侵地实现分布式事务功能。那么 lcn 模式是怎么实现的呢?

如上图,如果看过前文,非补偿的分布式事务方案:2PC,你就会发现。图中的过程其实就是 2PC 的思想。同样有 TM 这种角色,同样是两阶段。

第一阶段:

各参与方执行事务,准备提交。可见,此时TM已经知晓了有哪些参与者,并且参与者的准备提交状态也分别返回给了发起方。发起方和参与者是一样的,只不过等于单机事务的第一条事务语句而已。

第二阶段:

由发起方决定是否进行提交,然后 TM 依次通知参与者,最后通知发起者执行。

从这两个阶段来看,发起方 和 TM 占据非常重要的地位。

重点问题1:第一阶段的锁定资源、做提交准备是如何实现的?

这就是每个参与方在代码中依赖配置的 lcn 代理实现的。

执行数据操作代码的时候,会先执行,但是并不进行提交。因为 lcn 框架代理了数据库连接,所以可以伪提交。这就意味着 lcn 模式下,参与方(发起方)本地事务是一直处于未提交状态,直到整体系统完成了分布式事务。

重点问题2:同2PC一样,在关键步骤中,TM 或者 参与者 或者发起方挂了(通信超时),如何处理?

假如发起方在各参与者准备提交后,由于某些原因,导致发起方挂了,则参与方和 TM 就都不清楚该提交还是回滚。

参与者会先等待 TM 通知,如果超时到了则主动联系 TM 看如何处理。但是 TM 此时也一脸懵,所以会让 TC发起方继续等待。直到发起方重新上线,发起方会根据本地日志来通知 TM 提交或者回滚。

若第二阶段中,TM 通知完成所有的参与者以后,最后会再通知发起方。如果此时发起方挂了,TM 会记录补偿日志,并通知运维。

如果第一阶段中,参与方挂了,则发起方会通知 TM 发起回滚通知。

如果第二阶段中,发起方通知 TM 发起提交通知。如果此时一个参与者挂了,其他参与者还是会提交事务。TM 会重试通知挂的参与者,如果不行,则进行补偿机制,并通知运维。

如果是 TM 挂了,那么 TC(参与者和发起方) 就会超时重发,尝试联系 TM。如果不行,则会回滚本地事务。若之后联系上了 TM,则依据 TM 和发起方的沟通结果,执行提交或者回滚。

至于 TCC 模式,tcc 模式需要业务方实现 try(预留资源),commmit(提交),cancel(回滚)操作,与代码耦合度很高。但是好处是不依赖于底层数据库的实现,数据库无须实现事务机制,适用范围广。

LCN 框架提供的 TCC 模式,TM 和 TC 的控制流程和 lcn 模式相同。TC 执行业务就相当于调用 try 操作,TM 根据 发起方的反馈,去通知 TC 调用 commit 操作或者 cancel操作。

TC 执行 try 操作,因为有 commit 和 cancel的业务代码处理,所以会直接提交本地事务。这样,就不会长时间占用本地事务,提升性能。当收到 TM 的 commit 通知时,commit 的实现就可以不用再处理事务了,因为已经在 try 中提交了。

LCN框架的各种模式性能见 测试报告


基于如上的 LCN 框架的 lcn模式和 tcc 模式的关联对照,我们已经初步知晓了 tcc 模式的使用方法。接下来,再进一步看下 tcc 模式是如何应用的。最后,再谈谈 tcc 的一些特殊处理。

比如转账操作,扣款方的 tcc 实现中,try 直接扣款,然后 commit 就是空提交,cancel 操作则反向加钱。

在收款方的实现中,try 不进行增加余额操作,因为前面说了,tcc模式的预留资源就是直接提交,这样在第一阶段结束后本地事务就结束了。所以 try 如果执行了增加余额,则在 tcc 万一要回滚之前,这笔钱已经可以花了。所以,等确实需要提交了,就在 commit 方法中进行增加余额操作。这样子,cancel 方法也什么都不用做,因为 try 也没做增加余额操作。

说这些的目的是,虽然方法叫 try、commit、cancel,但是只要知道了他们的处理流程,实际业务的编写还是要灵活处理。

tcc 由于把重点都交给了业务端,所以我们还要针对一些异常来分析。


在比较流行的另一款事务框架 Seata 中,TCC 模式的实现还需要业务方注意以下问题:

  • 允许空回滚现象
  • commit/cancel 需要幂等
  • 防止资源悬挂

空回滚异常

指的是由于网络波动,导致 参与方 在没有执行 try 操作的情况下,却收到要执行 cancel 操作的命令。

分析 LCN 的 TCC 模式就会发现,参与者是先执行 try 之后,才会加入事务组,否则 TM 也不会通知到这个参与者,所以可以避免这样的空回滚现象。

幂等设计

这个很明显了,后端开发中一些接口自身就是要实现幂等性的。即多次调用和一次调用的效果相同。这里处理幂等性无非是利用一个唯一标识或者状态字段记录,以此来判断是否需要处理。

基于 LCN 框架实现,也是需要处理幂等性的。TM 会有超时重发机制。

防止悬挂

防悬挂就是参与者 cancel 已经执行,发起方认为整体事务已经正常执行完毕。但是由于网络阻塞的原因,此时 针对该参与者的 try 请求才到,这样参与者会执行 try 请求,但是这次执行并不在全局事务的掌控之中,就造成了资源hold住。

听起来很神奇吧,这在 Seata 中是会出现的。Seata 要求业务允许空回滚,那么当网络堵塞时,try 请求没到,但是由于超时机制,cancel 请求却到了,业务进行空回滚,所以全局事务正常结束。结束之后,try 请求才到,就会产生悬挂现象。

可以发现,LCN 框架是没有这种悬挂现象的。


下一篇,就是分布式事务的最后一篇,我们来看看其他的分布式事务实现方案。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oatlmy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值