几年前,设计了一套增值服务购买流程,当时设计一整套下单购买流程。当时看了我们项目中多个订单防重,发现都不一样。结合了一点自己的思考,设计了一整套流程。当时感觉没什么,面试一问全扒瞎,特此回顾一下。
重复提交是项目中最常遇到的一个问题。为啥?不好界定啊,比如
- 用户就是要买两单,手速快了点,防重了就不合适。
- 大商户就是要用工具刷,就突出一个合理。
- 前端忘记防抖,没辙。
- 接口失败重试,机制就是这个机制
- 队列重复消费了,也算是一种吧
会造成哪些问题呢?
- 比如重复扣减库存?
- 比如多加积分?
- 比如重复下单?
场景还有很多,一般核心的业务场景都是要做预防的,具体哪些就要根据业务场景来考虑了
至于怎么解决,首先有两个概念,防重和幂等
- 防重:防止重复提交,对于结果没有严格要求
- 幂等:通俗点来说是防重的子集,再防重的基础上要求返回的结果一致
实际场景分&析解决方案
一个精简版的下单流程就是下面这个样子,下面就根据这个场景来聊一下防重。不谈场景就谈方案都是***!!!!
就拿上面的场景来说,我标注的三个地方都有可能发生重复提交的场景
- 场景1:客户端重复网络抖动,前端重复提交,页面重复进入。此处的重复提交基本上都和客户端和网络还有用户脱不了关系
- 场景2:可能得点就是重复消费,失败重复调用的场景。此处防重一般是第三方来考虑,稍后我也会分析分析。
- 场景3:这个场景多半就是失败重试了。跟第三方,最大努力通知相关。
场景1 的解决方案
- client处理
一般来说,前端要做到流程不回退,防抖等操作,这个很重要,可以解决很大一部分重复请求
- 服务端处理
- 带上token
请求的时候,可以前端台上token,或者后台提供token服务来解决这个问题,此token要保证特定时间内唯一性
- 活锁CAS
如果不使用token也不使用redis,可以通过cas改变修改其他表的状态或者库存的方式,如果成功,才进行后续的订单创建
场景2 的解决方案
这个场景一般是三方服务要考虑的场景了,根据我对接调试的经验,一般是一种。
每次请求都生成一个订单,保证订单id唯一。每次请求都是新的一单,不会乱,出现问题也都是调用方的问题。一般的支付渠道也都是一次请求一单。
但是不能防止恶意请求的方式,为了解决这个问题,三方都会带上非对称加解密的方式进行请求校验。
场景3 的解决方案
第三个场景呢,也是在支付的时候会遇到的情况。有的支付渠道防控做的不大好,就是会有重复调用的情况。关键通知的时间不可控,一般来说是10分钟,20分钟,xxxxx进行累加。时间跨度比较大,也比较容易。
- 根据己方或者三方订单号进行查询,判断,这种可以防止时间跨度比较大的情况
- 唯一索引进行兜底,避免并发情况。
总结
当时设计的时候,防重部分,参考了项目中的蛮多案例的。这里只把一个下单流程中防重的点拿出来分析,适用性比较广。方案算不上高大上,但是适用性还可以,我觉得可以cover大部分场景,。
- 前端需要保证下单后流程不回退
- 唯一token校验防止重复
- 依赖于上游服务校验防止重复
- 每次下单都生成一单
- 唯一索引