文章目录
一:重复下单与幂等性问题
用户在下单页面进行下单时,由于用户点击下单按钮 多次 、或者 重试策略导致在订单服务中接收到了 两次同样 的下单请求。
什么情况下会重复下单
- 用户短时间内多次点击下单按钮
- 超时重试
- 用户APP强退/闪退之后重新下单
重复下单问题,本质上,就是下单操作的幂等性问题,说到底,就是接口幂等性。
二:如何解决重复下单问题
方案一:提交订单按钮置灰
从浏览器端
去拦住一部分请求,减少后端服务器的处理压力,达到过滤流量的效果。
方案一优点:简单。基本可以防止重复点击提交按钮造成的重复提交问题。
方案一缺点:前进后退操作,或者F5刷新页面等问题并不能得到解决。因为服务端这里没有加限制
方案二:请求唯一ID+数据库唯一索引约束(数据库层面做幂等)
- 当用户进入订单提交界面的时候,调用后端获取请求唯一ID,并将唯一ID值埋点在页面里面。
- 当用户点击提交按钮时,后端检查这个唯一ID是否用过,如果没有用过,继续后续逻辑;如果用过,就提示重复提交。
- 最关键的一步操作,就是把这个唯一ID 存入业务表中,同时设置这个字段为唯一索引类型,
从数据库层面做防止重复提交
。
缺点:DB层面约束,并发量低
方案三:redis分布式锁 + token 推荐
- 用户点击提交订单按钮,服务端接受到请求后,通过规则计算出本次请求唯一ID值
- 接口唯一ID:应用名+接口名+方法名+请求参数签名(请求header、body参数,取SHA1值)
- 使用redis的分布式锁服务,对请求 ID 在限定的时间内尝试进行加锁,如果加锁成功,继续后续流程;如果加锁失败,说明服务正在处理,请勿重复提交。
- 最后一步,如果加锁成功后,需要将锁手动释放掉,以免再次请求时,提示同样的信息。
思考:同一个用户可能就是想买两次相同的产品,可能说先买了一个,刚买完发现需要两个,再买一个同一个产品,所以这时候我们设计不能不让用户买,这种情况就是说能不能对唯一ID设计上来区分这次请求,如果说不能区分,那在我们业务上redis分布式锁的时间范围这要去设计准确点,比如说一次下单到支付需要多长时间,锁的时间设置为比这个时间稍大些,不能时间太长,这样用户第二次购买发现不让重复操作。当然前面只是一种思路,还比如能不能经过判断后唯一ID相同,但是不知道用户是不是想重复购买,这时候可以给用户提示:发现购买相同,是否继续,继续那就放行,不继续就拦截。
代码实现思路:使用AOP实现对业务token的无侵入生成
可以定义注解,在生成订单的方法上加该注解,AOP的逻辑就是生成唯一token,然后加锁,看加锁能不能成功,成功则放行执行后续逻辑,失败则表示重复下单,拦截。
万能方案
一锁二判三更新
一锁:先加锁,可以加分布式锁、悲观锁都可以,但是一定是一个互斥锁
。
二判:进行幂等性判断
,可以基于状态机
、业务流水表
、数据库唯一索引
等,进行重复操作的判断。
三更新:对数据进行更新
,将数据进行持久化。
三:总结
防止重复下单,本质上就是先做重复判断,然后服务端做好幂等性控制,结合实际业务场景选择相应的方案。