阅读 887

电商订单支付业务如何设计?

BASE, Basically Available(基本可用) 、Soft-state(软状态) 和 Eventually Consistent(最终一致性),EDA (Event Driven Architecture),相信大家都背的滚瓜烂熟了。但是,到底如何实现?

下面以电商订单支付业务为例,来介绍BASE/EDA如何实现。在微服务架构中,我们一般有“订单服务”和“支付服务”两个微服务。订单的支付状态有两个:“未支付”和“已支付”。在处理支付业务的时候,需要查询订单的状态,“未支付”的订单才能支付。未支付的订单在一定时间后自动取消(将预扣减的库存还回去)。

可能问题:

  • 如果用户正好在订单要自动取消前一刻开始支付,然后订单被自动取消,然后用户支付完成,然后就出现了不可思议的现象:用户支付成功了一个被取消的订单。。。
  • 用户支付成功后,马上刷新页面,由于订单系统可能没有及时更新订单状态,用户可能看到订单任然是“未支付”状态。

为了防止这种情况,我们引入一个软状态:支付中。 支付中是一个中间状态,它有若干作用:

  • 让支付中的订单不能超时取消,防止上面的“用户支付成功了一个被取消的订单”这个现象出现。
  • 用户支付成功后,马上刷新页面,用户看到的是“支付中”,而不是“未支付”。

什么是软状态(BASE中的Soft State)?

  • 软状态是在没有用户输入(操作),会自己改变的状态;
  • 软状态一般是中间状态;
  • 引入软状态的目的是为了控制业务流程。

如何设计支付业务?

如下图: ①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳

pay.PNG

  • ①支付服务同步调用订单服务,设置订单的状态为“支付中”
    • ②做幂等检查,如果是重复调用直接返回上次调用的执行结果
      • 接口参数中需要有幂等ID
      • 幂等ID可以是一个不重复的随机数,例如UUID
      • 记录调用历史,如果幂等ID已经使用过,说明这是第N次(N>1)调用,直接跑到⑤去,返回第一次执行的结果
      • 幂等需要考虑并发的情况,多个线程同时在执行,需要考虑使用分布式锁,推荐使用数据库乐观锁。
    • ③没有重复调用,检查订单状态。
      • 如果不是“未支付”,返回设置失败。
      • ④如果状态是“未支付”,在乐观锁(CAS,或者其它分布式锁方案)中设置状态为支付中,然后返回设置结果。 这里有可能返回失败(如果有锁竞争)。
  • ⑥同步得到支付中软状态设置结果,设置成功后才能进入后续支付流程。
    • ⑦ 幂等判断是否重复支付
      • 这里幂等ID为: 订单编号 + "_PAY"
      • ⑧如果有成功支付记录, 返回成功。
      • 没有成功支付记录,进入⑨支付流程
    • ⑨ 进入支付这个流程,这个流程包含若干子流程,例如调用支付宝/银行等支付业务等。
    • ⑩ 将支付结果返回给BFF,将“支付结果事件⑬”发送到消息队列里去
  • ⑭订单微服务收到支付结果,修改订单状态。
    • 如果支付成功,设置订单为“已支付”。(订单支付状态设计为单独字段:只有未支付/支付中/已支付3个状态。)
    • ⑯如果支付失败,订单状态为“支付中”,⑰将订单设置为“未支付”。
      • 此时订单状态有极小的概率为“未支付”或者“已支付”. 可能发生原因为消息重复,消息到达顺序错乱。这种情况维持原订单状态即可,不用修改。
  • 数据不一致处理
    • ⑪因为各种原因,有一定几率出现可能数据不一致(例如,EDA中的事件丢失),订单服务将状态为“支付中”,已经超过一定时间的订单号,发送给支付服务,要求订单服务补发支付结果⑫。
    • ⑫ 支付服务重新将支付结果查询出来,补发“支付结果事件⑬”
      • 如果支付系统中没有订单的支付记录,补发“支付失败”。可能原因:①设置订单为“支付中”的同步调用超时,网络异常等,后续流程⑥没有执行;执行后面的流程出现异常,甚至硬件失败等。
      • 如果订单的支付流程仍然没有完成,不做处理。(此时支付系统有严重问题。但是订单完成后会有支付结果消息给订单服务)

总结

在微服务架构中,由于多个微服务可能并行异步处理同一个业务。微服务之间不知道其它微服务的执行结果,需要引入软状态(中间状态)来控制业务流程的走向

微服务的接口可能被重复调用,需要实现幂等来保证业务正确性。重复调用的原因有两种:用户(可能因为系统响应慢而)重复提交;调用超时出现错误,调用方重试。不同的场景幂等的处理方式不一样。

事件可能会重复,可能会丢失,可能不能保证顺序。有的设计不良的事件机制甚至可能出现“虚假事件”。(保证不重复,保证不丢失,保证顺序的事件机制性能肯定很差,如果各业务都假设事件不重复,不丢失,顺序都正确,代码写起来简单了,但是出现事故的时候人肉处理起来就超级难了。不过好在可以让运维背锅。)(数据库和MQ服务器是两个数据源,他们有分布事务的问题。MQ提交数据,数据库回滚数据,“虚假事件”就出现了)

人算不如天算,总有各种天灾人祸导致各系统数据不一致。兜底方案要有,比如对账机制。⑪⑫提到的补发事件机制算一种自动对账的手段。实际情况可能还需要有人肉处理预案。

文章分类
后端
文章标签