方案一
这套方案采用的是跟京东、小米一样的流程,把秒杀当成一个优惠显示的信息,用户秒杀还是走正常的下单流程,添加购物车、创建订单、支付 ,
优点
把秒杀当成优惠信息,如果点加入购物车,走这一套流程,流量就分散开了,
还有如果使用整个加入购物车流程的话,整个业务其实是统一的,数据模型比较好设计,也就比普通商品的数据信息,多个秒杀价格,多个秒杀库存
缺点
分散开的流量会级联的映射到其他系统里边,本来秒杀请求可能只是秒杀系统扛不住,但是大量请求一进来,又在商品详情页展示,然后还要在购物车页面要确认,在订单页要确认,如果我们此时在极限情况下,用户都来访问的话,可能每一个页面都扛不住,除非流量非常分散,才可以扛得住非常大的并发。
方案二
如果要应对超高并发的流量,我们也可以选择这一套
用户点击“立即抢购”之后,请求发给秒杀系统,秒杀系统开始抢购,抢购系统会先做一系列的判断,比如登录,用户必须登录了才能抢购,然后再是合法性校验,这里面包括当前商品是否到了秒杀时间、随机码与 skuId 是否正确、用户是否已经秒杀过了,如果这些都成功了,再来获取信号量。假设要秒杀的商品库存只有400件,如果能获取到信号量,那就是秒杀成功,否则就是秒杀失败。
秒杀作为一个独立的流程,只要我们的定时任务能把它上架上去,上架多少个库存,底层的数据库就会锁多少库存。
获取到信号量之后,不走以前的下单流程,而是在秒杀服务里边快速的生成一个订单号,再把订单号、以及用户秒杀的这些信息,直接发给 MQ,处理到这,我们只发了一个消息,没有动过任何数据库。消息发出去之后,后台的订单服务会监听这个消息,然后创建这个秒杀订单,此时就可以通知前端的用户:“秒杀成功正在为您准备订单”,可以在订单的准备页,让它十秒以后跳转到支付页,支付之前让用户确认一下收货地址,选好以后,用户点击支付,最终前往支付确认。
优点
从请求一进来,到 Controller,再到 Service,有一堆判断处理,一直到后面的给前端用户通知,这期间没有操作过一次数据库,没做过任何一次远程调用,这就是一个非常快的流程,我们只需要校验好所有的合法性就行。因为所有的数据我们都在缓存里边放着。一切正常以后,我们给秒杀订单快速的创建一个单号。然后告诉前端个单号已经准备好了,后台的订单服务,在慢慢的消费。
缺点
如果说在极限情况下,订单服务都炸了,有个用户在秒杀,秒杀系统告诉他秒杀成功了,MQ 发出去了,但是却没有消费者来消费这个订单,没有人消费的话,就会导致订单的后续信息准备不好,用户一直支付不成功,所以这一块的后续处理,跟前面的整套业务又不一样,需要加入一些独立的业务处理能力。
最终
选择第二种方案
到这单机,单线程也就是花费20ms,相当于每个线程每秒能容纳20个请求,如果能让tomcat接收500并发的请求,那500*20,每秒的并发量就能达到1W
如果换成那种正常下单的流程,一个线程都得执行3秒,就算让tomcat接收500并发的请求,那1秒也顶多处理三分之一的请求,相当于每秒的并发量只有200
代码
后续实现
[x] 保存订单时,保存全量数据
[x] 上架秒杀数据时,指定秒杀场次的过期时间
[ ] 上架秒杀数据时,指定秒杀商品的过期时间,这个属实不知道怎么给 redis 中 hash 结构的某个 key 指定过期时间,有知道的大佬,也可以提示一波
[x] 上架秒杀数据时,指定随机码的过期时间
[x] 上面的超时未支付、或者放弃支付,导致的回填信号量以及删除用户的抢购数据,我是在秒杀服务,又创建了一个队列,然后秒杀服务监听那个队列实现的,类似库存服务解锁库存那样。