为什么要做秒杀?这个不难解释,最起码对于互联网电商业务来说很常见,那怎么样才能设计出相对比较完善的秒杀策略呢?我觉得这其中有两个关键点:
谈到秒杀,我们的第一反应就是多人次抢一款或者几款产品导致瞬间产生的流量峰值很大,那如何支持高并发就是其中的重点之一。
库存怎么来锁,每件商品有限定的秒杀库存数,我们怎么来变更库存信息,MySQL数据库直接操作么,又或者是通过Java中的原子性类来维护库存信息?
下面我来谈谈我们的实践。
整体系统架构
Web层,主要是APP、HTML 5、PC端的用户流量,目前是7:1:2 流量占比。
反爬和网关是公司层面的用于反爬虫以及网关路由。
Restful API主要是用于收集搜索、产品详情、秒杀信息,用于后端跟前端模板的一个映射。
SecKill API主要是用于访问产品的库存信息以及产品的秒杀信息,独立出秒杀API对原有的产品API下单服务不影响,减少原有系统的耦合。
Booking和Order API我就不分开来讲,当秒杀成功以后用于生成订单。
大胆设计
关于怎么支持高并发有两种策略可以结合,通过前端限流机制只放10%左右的流量到后端,90%的人直接提示秒杀结束,下面我主要是讲讲后端怎么实现。我想Redis大家都用过,其高并发能力超强,理论峰值是单机每秒能支持10万次读写,Redis还可以支持分布式集群扩展性强。还有一点Redis更新操作是原子性的,更新数据是单线程的安全有保证。锁定库存就用Redis来实现,大致的流程如下:
流程梳理
预热缓存,即将产品信息以及库存信息刷新到缓存之中,难道只存这些信息么?这两项是最主要的,其它附加的后面会讲到。
后台应用接收前端的访问,我为什么要明确画出Tomcat容器,这个后续也是也有用的。
通过产品信息为key,去对Redis库存信息执行库存减操作。
有个long型值返回。
判断返回值是否大于或者等于0。
执行到这里说明该用户应该是可以下订单购买的。
直接操作insert DB?没错像订单这么重要的信息,还是应该落库为安。
秒杀商品库存不足,直接返回秒杀结束。
缺陷思考
在缓存预热好,秒杀开始之前有流量进来怎么办?或者秒杀已经库存为0应该结束秒杀怎么办?为了解决这个问题我们应该在步骤1的时候添加秒杀开始和秒杀结束时间,作为操作8开始之前的第一道判断请求。如果在开始之前以及在结束时间之后则直接返回秒杀未开始或者秒杀已经结束。所以更为完善的流程图应该如下:
操作11中,得知有库存直接下订单会不会真对数据库造成很大压力呢?肯定会的,所以先期可以减少秒杀的产品数以及库存数量,如果调用订单接口插入订单数据失败,应该释放库存信息,让请求重新可以秒杀产品信息。
如何评估应该部署多少台应用服务器和Redis集群?单台Tomcat容器的每秒访问上限是1000左右,单台Redis机器低估一下每秒也可以抗1万次读写。举例来说,如果秒杀的每秒的访问是1万,部署10台应用服务器,一个Redis集群,2-3台Redis服务器(可写的redis节点)理论就都可以了(这边峰值计算容器级别的Redis级别需要分开来算,因为先需要访问Tomcat应用再去访问Redis,Tomcat也有连接数上限)。当然一个整个流程下来其实是不需要1秒的,大致100毫秒左右,所以理论抗并发峰值应该是可以抗1万 * 10左右。