两阶段提交在提现业务中的应用

155 阅读2分钟

愿景

愿天下没有难写的代码

背景

在虚拟币提现业务中,用户发起提现,需要扣减用户余额,并写入提现单。

这里涉及到账号服务、订单服务一致性问题。

例如:扣款完成,但是订单未创建;或者订单创建,但扣款未完成。

该业务的基本流程如下

image.png

下面介绍,如何使用 2阶段提交的方式,来解决分布式事务问题。

虽然使用分布式事务框架也能解决该问题。但分布式事务框架不在本次讨论范畴之中。

业务问题描述

上诉业务流程,存在以下几个问题:

  1. 订单创建后,如果扣款失败怎么处理?

  2. 如果用 RocketMQ 的事务消息去实现,即生成订单作为本地事务,但是扣款则会变成异步,那么业务上,异步扣款是否可行?

  3. 扣款成功了,订单服务准备提交事务时,应用程序 crash,导致订单未创建成功,如何处理?

扣款失败,回滚事务?

扣款失败,是否可以直接回滚事务呢?

明确的告诉你:不能。

扣款失败有以下几个原因:

  1. 调用超时

  2. 真的失败

如果是调用超时,没办法确定账号服务是否扣款成功,因此不能回滚事务。

异步扣款?

从业务角度来看,不推荐异步扣款。

异步扣款会导致即使余额不足,也可以发起提现。

2阶段提交

我们可以利用 2阶段的思想 将业务流程拆解为如下

image.png

步骤1步骤2步骤3步骤4最终结果
成功成功应用程序 crash, 订单状态仍为 prepare补偿,订单被修改为成功提现成功
成功成功执行成功,订单状态被修改为提现不执行提现成功
成功失败应用程序 crash, 订单状态仍为 prepare补偿,订单被修改为失败提现失败
成功失败执行成功,订单状态被修改为失败不执行提现失败
成功扣款超时,实际扣款失败不做任何处理,订单状态仍为 prepare补偿,订单被修改为失败提现失败
成功扣款超时,实际扣款成功不做任何处理,订单状态仍为 prepare补偿,订单被修改为成功提现成功

综上所述来看,我们通过两阶段提交的思想,简单的实现了最终一致。

如果你对 Mysql redo 2阶段提交 过程熟悉的话,你会发现上述的流程其实跟其大差不差。