TCC分布式事务简单理解

写这篇博客的起源

周末在外逛街,偶然听到路人在讨论TCC的项目实战,显然我和他们是同行,于是我回顾一下自己对TCC的理解。如果说没有做过类似电商的项目,TCC可能会接触的比较少,接下来我就用大白话画图的方式来简述我对TCC的理解,希望对大家有所帮助。

业务场景

这是一个电商系统的系统小模块,客户下单订单支付成功后的场景:

  • 订单支付成功后修改订单状态为"已支付"
  • 通知库存系统扣减库存
  • 通知会员系统增加客户的积分
  • 通知仓库系统生成出库单给客户发货
    以下是基础的调用图,应该是比较容易理解的
    在这里插入图片描述

业务需求

订单服务:修改订单状态
库存服务:扣减库存
积分服务:增加积分
仓库服务:创建出库单,让仓库发挥
以上几个步骤,必须保证要么同时成功,要么同时失败,简单举个例子,现在订单的状态都修改为“已支付”了,结果库存服务扣减库存失败,原本库存是100件,扣减该订单的1件,实际上库存剩余99件,但是,由于库存服务异常,库存数量还是100件,这就可能导致超卖了,电商系统肯定是不允许这样的情况发生的。所以这里有必要用TCC分布式事务来保证各个服务的形成一个统一的事务。

业务实现

1.TCC实现阶段1:Try

public class OrderService{
	@Autowired
	private InventoryService inventoryService;//库存服务

	@Autowired
	private CreditService creditService;//积分服务

	@Autowired
	private WmsService wmsService;//仓库服务

	@Autowired
	private OrderDao orderDao;
	
	public void doPay() {
		//修改订单状态为已支付
		orderDao.updateStatus(OrderStatusEnum.PAY);
		
		//调用库存服务扣减库存
		inventoryService.reduceInventory(quantity);
		
		//调用积分服务增加积分
		creditService.addCredit();

		//调用仓库服务生成出库单
		wmsService.pushWms();
	}
}

上面这段代码只是简单的模拟一下,具体项目的实现代码肯定不是这样的,但是基本的思路应该都是一样的,就是订单服务完成状态更新之后通过SpringCloud的Feign来调用其他的各个服务。
但是上面这段代码不可能实现TCC分布式事务,别急,我们慢慢往下优化:
首先,我们在订单服务中把订单状态修改为OrderStatus.UPDATING,看到这里是不是有疑问了?
换句话说,就是在doPay(),把订单状态修改为修改中,而不是已支付,这个修改中的状态只是一个中间的状态而已,代表这个订单正在修改,并没有其他特别的含义;然后呢,在库存服务提供的那个reduceInventory()里也不要直接扣减库存,可以理解为冻结库存,我的上家电商公司库存就是分为可用库存和冻结库存,熟悉WMS系统的同学应该会很好理解,还是举个例子吧,原本库存是100个,可销售库存(后面简称可用库存)为100-1=99个,另外冻结库存设置为1;以此类推,在积分服务里不要直接给客户增加积分,可以用另外一个预增加字段保存要增加的积分,仓库服务可以先创建一个出库单,但是出库单的状态可设置为草稿状态(只是一个模拟的状态,目的是不让出库单发货)。
以上接口的改造过程,其实就是TCC分布式事务中的第一个字母T的代表含义:Try,那我们将上面那张图稍微改造一下:
在这里插入图片描述

2.TCC实现阶段二:confirm

执行第一阶段无非就两种情况,这种情况就是各个服务执行的Try操作,并且都执行成功了,万事大吉。既然Try都执行成功了,那怎么执行第二阶段confirm呢?

这个时候就需要依靠TCC分布式事务框架了,也就是说项目里必须引入一套TCC框架,目前比较流行的开源框架有:tcc-transaction、Hmily、ByteTCC、EasyTransaction等等。当然,如果你足够优秀,自己手写一套也不是不可以。

如果你在各个服务里引入了一个TCC分布式事务的框架,订单服务里内嵌的那个TCC分布式事务框架可以感知到,各个服务的Try操作都成功了。此时,TCC分布式事务框架会控制进入TCC下一个阶段,第一个C阶段,也就是Confirm阶段。
首先,我们要在各个服务里加入confirm的逻辑代码,例如,在订单服务里加入的逻辑就是把订单的状态由修改中更新为已支付:

public class orderConfirmService{
	public void pay(){
		orderDao.updateStatus(OrderStatusEnum.PAY);
	}
}

库存服务也是以此类推,提供confirm逻辑,将冻结库存字段的库存数直接扣掉变为0,这样库存就真正扣减没了。积分服务提供confirm逻辑将与增加积分字段的数值加到积分上,同时将预增加积分字段值置为零,这样积分就处理完了。同理,仓库服务直接将出库单的状态修改为可出库的状态,让仓库同学能看到出库单发货就行。
confirm的逻辑都实现好了,一旦订单服务里的TCC感知各个服务的Try阶段都成功了以后,就会执行各个服务的confirm逻辑。

3.TCC实现阶段三:cancel

上面介绍了Try会有两个结果,现在这个结果肯定就是失败的情况了,失败的情况,肯定是各个服务的逻辑回滚,一旦有服务的Try逻辑执行失败,订单服务的TCC是可以感知到的,然后就会执行各个服务的cancel逻辑,同理为了实现cancel逻辑,我们也需要在各个服务里增加cancel的逻辑代码。
其实逻辑都是以此类推的,比如订单服务的逻辑:

public class orderCancelService{
	public void pay(){
		orderDao.updateStatus(OrderStatus.CANCEL);
	}
}

其他服务我就不再赘述了,都是一样的逻辑,相信大家都能够理解。

4.总结

如果想要实现分布式事务的话,首先要选择TCC框架,各个服务就会有这个框架运行;其次就是接口改造,原本一个接口,需要改造成三个逻辑:try – confirm – cancel

  • 服务依次调用各自的try逻辑
  • 如果全部try逻辑都成功的话,TCC就会执行confirm,完成整个事务
  • 如果try逻辑有异常的话,TCC就会执行cancel,将整个事务全部回滚,撤销之前所有操作
    其实这就是分布式事务,至于TCC分布式事务的使用场景我就不赘述了,这只是我对分布式事务的简单理解。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值