对于一名后端工程师来说,实现一些用户下单购买商品的需求是很常见的。最近刚接了一个与购买商品相关的迭代需求,也踩了一些坑,所以跟大家分享一下坑点。
服务
假设现在要下单购买一只哈士奇,那么后端需要提供哪些服务呢?
- 处理哈士奇业务逻辑相关的服务,该服务负责校验用户能否购买哈士奇,并最终返回用户一个购买链接。(以下简称哈士奇服务)
- 订单服务,在哈士奇服务校验通过之后,哈士奇服务会向订单服务发起一个创建订单的请求,成功以后这份订单会在订单服务中落到数据库。
- 支付服务,该服务主要有两个作用,一是根据订单id生成特定的购买链接,二则是提供用户支付的接口。
哈士奇服务与支付服务比较简单,这里按下不表,重点说一下订单服务,稍微对订单业务有一点了解的朋友都知道,订单服务是一个典型的状态机服务。一张订单有多个状态,每当订单状态发生变更的时候,订单服务都会作为一个Producer将状态变更的消息publish到消息队列中,方便Consumer订阅变更消息。
常见的订单状态如:created(创建),pay_success(支付成功),pay_failed(支付失败),confirmed(业务方确认),refund(退款)。下面通过图1-1表示订单的状态流转:
场景
在了解购买一只哈士奇所需要的服务之后,我们来看看如何通过实际场景将这些服务联系起来。
下单购买
哈士奇服务(负责业务逻辑)可以说是用户最常打交道的服务,因此用户向哈士奇服务请求生成一条购买链接,整个下单流程可以概括为三个步骤,validate -> make_order -> return_pay_url。流程图如图1-1:
validate主要是哈士奇服务负责的业务逻辑校验,这里通常由业务开发者自己定制,我们重点关注一下make_order和return_pay_url的子过程。
make_order
return_pay_url
创建支付链接的流程就非常简单了,如图2-3创建支付链接流程:
支付成功
当用户从哈士奇服务得到支付链接之后,最常见的操作就是直接支付了,此时用户面对的应该是支付服务的接口。
哈士奇服务通过监听支付成功的消息队列,进行业务逻辑的回调处理。
坑点
虽然上述的哈士奇服务处理支付成功订单的流程非常简单,但是细节的处理也比较多。
- 当哈士奇服务收到消息时,一般都通过消息队列异步调用自己服务的回调函数,这是因为订单服务的消息不会二次传达消息,超过N分钟没有被confirm的订单则自动被标记为失败。假设哈士奇服务收到消息之后立即处理,但是在提交事务的时候DB抖动导致提交失败,这么订单支付成功的消息就无法被正确消费了,如果之后哈士奇服务没有重试,将导致用户本来支付成功的订单变成支付失败的状态,因此哈士奇服务需要通过异步消息队列确保支付成功的消息被消费。
- 即使保证了哈士奇服务能正确处理支付成功的信息,仍然不能保证支付成功的消息在消息队列传输的过程中丢失,因此哈士奇服务需要维护一个定时补偿脚本,每隔一段时间就去订单服务中查询是否还有没有被confirm的订单,如果有则进行补偿处理。