关于分布式事务,网上有很多相应的解决方案,下面我们介绍一下在 Tom 项目中使用的一种解决方案,其不依赖第三方框架,并采用异步补偿回滚的方式保证事务的一致性,具体流程如下图所示。
首先作为调用方的交易中台会根据已经生成的订单号请求订单中台新增插入一条状态为未结束的订单创建
的事务记录。若在这一步操作就失败,那么订单创建流程就结束,否则开始调用商品中台冻结商品库存的操作。
冻结商品库存的操作可能会出现失败或者实际成功但是因为网络响应超时导致的失败,但是对于交易中台来说没法确认商品中台是否真正冻结商品库存成功,所以一旦调用失败调用方一律认为操作失败,此时创建订单流程结束。此时定时任务会定时捞取状态依旧是未结束的订单创建事务记录,并尝试去回滚商品库存。
需要说明的是由于交易中台没办法感知到商品中台是不是真的没有冻结商品库存成功与否,所以在解冻操作时,需要商品中台保证解冻操作的幂等。假设商品已经冻结成功,那么就进行解冻回滚。若商品之前没有冻结成功或者不存在该笔订单的冻结记录,此时商品中台需要做兜底操作并依旧响应解冻成功。如果请求营销中台也出现冻结优惠券失败的场景,也进行类似的回滚操作。等所有的回滚操作完成之后,再将订单创建事务记录的状态更新为已结束。
假设冻结商品库存和优惠券等操作都成功,此时开始真正创建订单信息并更新订单创建事务记录的状态为已结束,当然我们需要保证这两步操作的事务一致性。也由于都是在订单中台操作,因此我们可以保证操作的事务一致。当然如果这一步也失败,那么还是同理可以由定时任务回滚之前的冻结操作。
为了避免在冻结操作之后修改订单创建事务状态为结束之前,也就是上图左侧步骤 1 和 步骤 4 之间,订单创建事务记录被定时任务捞取到触发解冻操作,需要在插入订单创建事务记录时新增一个重试时间字段 retry_time,其值需要取当前时间加上指定时长,例如 60s。同时在定时任务查询获取未结束的订单创建事务记录时,需要指定 retry_time 小于当前时间。因此定时任务每次获取的事务记录一定是 60 s 之前所创建的。
60 s 是一个估计值,其默认假设上图左侧步骤 1 到步骤 4 所执行的时间不会操作 60 s。当然,具体值需要按实际项目具体情况来评估约定。
至此,这就是在未使用分布式事务框架的前提下实现的一种订单创建分布式事务解决方案。