写点什么

关于电商秒杀系统中防超卖、以及高性能下单的处理方案简述

  • 2021 年 11 月 11 日
  • 本文字数:1935 字

    阅读完需:约 6 分钟

譬如定在了周二 10 点开启抢购,那么在之前的一周时间内,都会有预约通知,或者普通的用户浏览。通过预约量、浏览量等数据分析,大概能预估到在周二会参与“点击抢购按钮”的人数。譬如有 500 万。


此时,我们是知道实际商品数量的,譬如 20 万。


那么我是没有必要让这 500 万个请求都到后台的,我最多最多放 200 万个请求到


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


后台。其他的 300 万直接就在前端网页看单机动画就好了。


这一步做起来很简单,20 万个商品,我提前生成 200 万个 token,在用户点击预约、或者浏览该商品时,就按规则发放出去。(规则可以是譬如公平模式,某个用户 id 已经预约多次了,还没抢到,那么给他 token。也可以就是随机发放,5 天的预热时间,每天发 4 万个就好)


前端接收到是否能参与秒杀的反馈后,就保存在浏览器本地就好,当秒杀开始时,没得到 token 的用户,就只好在本地看单机动画,过几秒告诉他商品不足就好了。


那些幸运的得到了 token 的用户,就有了给后台发请求参加秒杀的机会了,此时还需要前端(APP 客户端)来对请求进行控制,因为用户喜欢反复点击、反复刷新页面等手段来参加抢购,这时就不能再放重复请求进后台了,哪怕是他重复点击了,也要保证请求不反复发送。


对于大部分吃瓜群众来说,只会操作页面的就通过这种方式控制,但对于程序员们就不行了,即便是你在抢购开始前,没有暴露抢购的接口,但在抢购开始的一瞬间,他们依旧能搞到你的下单接口地址,并开始用程序频繁提交下单请求。


第二步 由网关限制程序过量请求




用程序下单对程序员们都懂,拼接好请求的各个参数,开启并发提交到服务器。


到了这一步,已经不归前端管了,请求会直达负载均衡器,然后到后台网关。


在网关里要控制好这部分请求,要以最快的速度判断出来的每一个请求是否放行到后面的服务。


网关的实现方案有很多,kong(nginx+lua),Gateway,zuul 等。在网关里可以简单的实现限流机制,我们主要限制的有如下几种:


1 黑名单(ip、用户 id 等),可以直接放内存里


2 过多的重复请求(可以采用 redis 集群计数,对同一个 ip、id 发起的重复请求给予拒绝),考虑到 redis 的带宽、性能瓶颈,可以考虑做分片,或者做二级缓存,直接在 jvm 内存里统计计数


3 没有 token 的请求,就是之前放出去那批 token


限制了非常规请求后,我们假如还有 100 万个请求在 2 秒内打到了服务端,这依旧是非常恐怖的数字,即便你有 10 台服务器,还是有大概率被打满 CPU,后面的请求就有面临 5 秒超时的风险。


此时,我们要做的就是尽快处理完前面的请求,把商品赶紧卖光。100 万个请求,20 万个商品,那肯定是不能让那 80 万请求去触碰下单的服务的,我们要在网关处就终结掉这 80 万个请求,给他们交代你来晚了。


此时你需要令牌桶,如 guava 的 rateLimer 就可以,简单好用。譬如我有 20 个 zuul 网关服务在运行,单个服务要承担 5 万个请求,单个 tomcat 在不做复杂计算、不做数据库操作,做到 1-2 千的 QPS 还是可以的。


我每一个 zuul 服务里譬如开辟 1.5 万个令牌桶,在 1-3 秒内放完,得不到令牌桶的就直接返回失败就行了。在这一步失败的耗时会很短,因为在网关层就失败了,不会进入到后面的下单流程。


请注意,这一步是没有用消息队列的,因为大部分请求是要被拒绝的,需要尽快的返回拒绝信息,进队列再慢慢消费就慢了。


令牌桶签发完毕,剩下的请求都是幸运儿,就可以进入到后面的下单流程了。


第三步 极速下单




下单是另外的服务,由 zuul 将请求转发到这里,那么以最快的速度生成订单将非常重要,不然又是大量超时。


此时数据库是指望不上了,数据库一秒 2 千的写入都已经比较艰难,即便是集群,想要达到万的量级也是比较困难,等你入库完毕,都半分钟过去了。


那么下单到哪呢,首选 redis。你在订单请求到达后,迅速拼接好 order、orderItem 对象,将订单下到 redis 里。考虑到 redis 的压力,可以将 redis 分片,将不同的用户的订单,下到不同的 redis 实例中。


下到 redis 的目的一是速度快,二是为了做订单查询用,因为下单后用户还是要查询订单的,而此时还没有入库。在下单到 redis 的同时,写入到消费队列 MQ 中一份,这一步是用来让后端消费,并入库的。入库就可以从 MQ 里慢慢消费了,再去做那些耗时的入库操作,分布式事务等等。入库成功后,就可以把 redis 的订单删掉了。


第四步 防超卖




从上面的流程看,我们通过令牌桶放出去的令牌数是大于商品数量的,那么就面临超卖问题。


超卖在分布式环境下,方案就是分布式锁,譬如 redisson 的分布式锁,可以针对商品 id 加分布式锁。


问题又出来了,如果商品数量很少,几百几千个,通过分布式锁也能很快的处理完。实测,redis 加锁、释放锁耗时约 1ms,再加上客户端逻辑处理时间,按下一单要 5-10ms(非常急速了),那么一秒在对同一个分布式锁的操作上,也就百单而已。

评论

发布
暂无评论
关于电商秒杀系统中防超卖、以及高性能下单的处理方案简述