1、场景假设
为了模拟分布式事务,我们现在假定有3个微服务,分别是:
- 订单
- 库存
- 会员
调用的业务逻辑如图下所示:
这里涉及到的一个分布式事务问题是:当扣余额失败之后,库存必须恢复到原来的数量而且创建的订单必须撤销。
2、Tcc分布式事务原理
tcc也叫二阶段提交,第一阶段是try。第二阶段是confirm或者cancel。
在第一阶段try,让所有人确认:”资源足够吗?并预留资源”。
如果所有的参与者都回答没有问题,接着便会进行第二阶段confirm,去真正的执行业务(比如扣库存)。
在try的过程中如果发现有问题,则会触发另外一个第二阶段方法cancel,去释放在try阶段预留的资源(预留是什么意思,后面会讲到)。
因此对所有的参与者我们必须实现三个方法:
- try:预留资源,确认资源可用。
- confirm:使用预留资源,真正去执行业务。
- cancel:取消预留资源。
大家可以看到网上很多都是这样写的,但是很多同学反馈上面的字都认识,但是连住一起就是不懂什么意思。我下面使用一个实际的例子,结合伪代码
带大家走下整个实现流程。
不管是使用什么tcc框架(ByteTCC、Himly、Seata),只要是使用了tcc的方式,其中的原理都是一样的。接下来以Himly框架为例讲解。
2.1、 订单微服务
1 |
|
在第一步调用生成订单
时,调用createOrder
方法实际上是执行try,检查资源。在try方法上通过注解@HmilyTCC
注解绑定了另外两个方法confirmCreateOrder
、cancelCreateOrder
,分别对应confirm和cancel。这两个方法会被tcc框架在合适的时机自行调用。对于confirm来说,需要等所有的try都执行完毕(也就是分布式事务发起者执行完毕),才会开始调用所有的confirm方法。
2.2、 仓库微服务
1 |
|
这里的try阶段做了2个重要的事情:
检查库存是否充足
把真实库存减掉,加到冻结库存中去。
为什么要减真实库存?
因为在第一阶段(try)扣掉的库存是不确定的,只有执行到第二阶段才能真正知道是不是该扣库存。因此如果try阶段不减真实库存,很有可能会超售。
为什么要冻结库存?
在cancel
的时候,你可以通过冻结库存知道该如何恢复正确数量到真实库存。
2.3、 会员微服务
会员微服务的代码和仓库微服务差不太多,就不在赘述了。
3、一些问题
冻结字段似乎无法处理多个人同时购买一个商品的情况。此时冻结库存会把多个人的库存量搞混。
TCC自身还有一些问题需要处理,如:幂等处理、空回滚和资源悬挂。
这些问题将在下期谈到如何解决。