亿级流量秒杀系统,亿级流量网站架构电子书

  

  本人从事电商行业十几年,经历过大大小小的促销活动和秒杀上百次。我每做一次秒杀,瞬间的流量就会增加几十倍甚至上百倍。对系统架构是极大的考验,也经历了系统宕机,甚至整体雪崩。   

  

  来自Pexels的图像   

  

  那么如何设计秒杀系统才能保证其高性能和稳定性,同时保证日常业务不受影响呢?   

  

  先看看秒杀场景特点:在秒杀开始前几分钟,大量用户开始进入秒杀产品详情页,很多人开始频繁刷新秒杀产品详情页。这个时候,秒杀产品详情页的访问量就会猛增。   

  

  秒杀之初,大量用户开始抢购。这时候造单和扣库存的压力会明显增加。其实秒杀场景基本上都是很多秒杀参与者,但是成功秒杀的很少,往往是几十万甚至更多的人抢几百只商品股。   

  

  那么我们是如何设计道钉系统的呢?主要涉及以下几个方面:   

  

  秒杀业务流程上的考虑   

  

  因为参与秒杀的产品售价很低,基本都是“各取所需”,下单成功后不付款的情况非常少。   

  

  所以我们采取的是下单时减少库存的方案,下单时会减少库存,然后付款。有个别订单不付款怎么办?   

  

  没关系。秒杀活动的主要目的是吸引流量。未能支付单个订单对秒杀活动本身几乎没有影响。   

  

  更何况,未付的存货还可以继续作为普通商品出售。不过要注意对机器人和自动脚本的防御,后面会详细介绍。   

  

  页面静态化   

  

  “秒杀开始前几分钟,大量用户开始进入秒杀商品详情页,很多人开始频繁刷新秒杀商品详情页,因此秒杀商品详情页的访问量会猛增”。   

  

  如果所有的请求都到后端服务,后端服务的压力会非常大(后端服务要处理业务逻辑,还要访问数据库,所以吞吐量比较低)。   

  

  考虑到秒杀是运营学生事先安排好的活动,在秒杀活动开始前就已经确定了要秒杀的商品、商品价格等信息。   

  

  所以我们可以把秒杀商品的详情页做成静态页面,把商品详情、商品价格等参数、评论、评价等所有信息都放在这个静态页面里,然后把这个静态页面上传到CDN进行预热。   

  

  CDN是一个内容分发网络,可以简单理解为互联网上的一个巨大缓存。它用于存储静态页面、图片、视频等。可以显著提高访问速度,用CDN承载流量,让商品详情页的大量访问请求不需要访问自己的网站(源站)。   

  

  这样既能提高访问速度,又能减轻网站带宽压力。   

  

     

  

  请求拦截   

  

  在首页,点击相关按钮,并把它们转回来,以防止重复提交。   

  

  在网关(Zuul,Nginx)层,为了避免前端的恶意请求,比如一些攻击脚本,在网关层,要用userID来限制点餐等接口的电流,几秒钟才能访问一次。   

  

  考虑到秒杀场景参与者众多,成功秒杀的非常少,我们可以在网关层直接拒绝大部分抢购订单的请求,作为秒杀失败处理。这大大减轻了后端服务的压力。   

  

  假设峰值库存是200,我们只能向后端服务发布200个请求。注意,为了尽可能避免库存被机器人和自动脚本抢走,秒杀开始时不能同时释放200个请求,可以分段释放。   

  

  比如,秒杀开始后,随机选择100ms内的5个请求释放(100ms内的其他请求直接拒绝,秒杀失败),然后每100ms释放5个请求,4秒可以释放200个请求。   

  

  分段发布,除了限制机器人和自动脚本,将请求分散在各个时间段,进一步缓解后端se的压力   

  

  另外,spike过程中的网关压力会比较大,可以把网关做成一个集群,多个节点分担访问压力。   

  

     

  

  后端服务设计   

  

  如果秒杀库存只有200,就会被网关拦截,再加上分段释放的方式。后台服务基本没有压力,日常后台服务完全可以支持秒杀活动。不需要做更复杂的设计。   

  

  但如果有几万个秒杀股,几万个发布单,为了用户体验,总发布时间不能太长,那么后端服务应该怎么设计?   

  

  这个时候主要压力在数据库。扣除库存压力,制造订单压力。   

  

  可以将库存放入Reids缓存中,以提高扣除库存的吞吐量。对于热门商品的库存,可以使用Redis进行碎片化存储。   

  

  订单创建可以采用异步消息队列。后端服务收到订单请求,直接放入消息队列,监控服务取出消息,订单放在最前面。   

信息写入 Redis,每隔 100ms 或者积攒 100 条订单,批量写入数据库一次。

  

前端页面下单后定时向后端拉取订单信息,获取到订单信息后跳转到支付页面。

  

用这种批量异步写入数据库的方式大幅减少了数据库写入频次,从而明显降低了订单数据库写入压力。

  

  

隔离

  

业务隔离

  

从业务上把秒杀和日常的售卖区分开来,把秒杀做为营销活动,要参与秒杀的商品需要提前报名参加活动。

  

这样我们就能提前知道哪些商家哪些商品要参与秒杀,可以根据提报的商品提前生成静态页面并上传到 CDN 预热,提报的商品库存也需要提前预热,可以将商品库存在活动开始前预热到 Redis,避免秒杀开始后大量的缓存穿透。

  

  

部署隔离

  

秒杀相关服务和日常服务要分组部署,不能因为秒杀出问题影响日常售卖业务。

  

可以申请单独的秒杀域名,从网络入口层就开始分流。网关也单独部署,秒杀走自己单独的网关,从而避免日常网关受到影响。

  

秒杀可以复用订单,库存,支付等日常服务,只是需要一些小的改造(比如下单流程走消息队列,批量写入订单库,以及在 Redis 中扣减库存)。

  

  

数据隔离

  

为了避免秒杀活动影响到日常售卖业务,Redis 缓存需要单独部署,甚至数据库也需要单独部署!数据隔离后,秒杀剩余的库存怎么办?

  

秒杀活动结束后,剩余库存可以归还到日常库存继续做为普通商品售卖。数据隔离后,秒杀订单和日常订单不在相同的数据库,之后的订单查询怎么展示?

  

可以在创建秒杀订单后发消息到消息队列,日常订单服务采取拉的方式消费消息。

  

这时日常订单服务是主动方,可以采用线程池的方式,根据机器的性能来增加或缩小线程池的大小,控制拉取消息的速度,来控制订单数据库的写入压力。

  

网络

  

秒杀前要和网络运营商、CDN 服务商提前申请带宽。

  

还有哪些细节要考虑:

  

①如何避免超卖?如果在 Redis 中扣减库存,可以利用 decr 命令扣减库存,decr 是原子操作,在分布式环境下也不会有并发问题,decr 扣减库存后,判断返回值,如果返回值小于 0,扣减库存失败,秒杀也就失败了。

  

如果在数据库中扣减库存可以在 where 后面加上库存大于 0 的条件,来避免库存被减成负值。这样就可以避免超卖情况发生了。

  

②接口防刷,前面已经提到过,在网关层对下单等接口按 userID 限流。

  

③网关层除了对 userID 做限流外,还要做整体限流。在实际访问量超过预估访问量时,整体限流可以起到保护作用,避免系统被压垮。

  

④防止重复下单,按 userID 限流已经起到了防止重复下单的作用。假如限制同一个用户 10 分钟能下一次单,一般情况下 10 分钟内,商品早已经被抢光了,用户也就没有再次下单的机会了。

  

⑤可以结合风控系统,在网关层把羊毛党等有问题的用户请求直接拒掉。

  

⑥可以在网关层上面再加一层防火墙或者高防服务,来防御 DDos 等分布式网络攻击。

相关文章