<h4>背景:</h4>
为什么要做秒杀?这个不难解释,最起码对于互联网电商业务来说很常见 ,怎么样才能设计出相对比较完善的秒杀策略呢:
1、谈到秒杀,最大的就是多人次抢一款或者几款产品导致瞬间产生的流量峰值很大,如何抗高并发
2、库存怎么来锁,每件商品有限定的秒杀库存数,我们怎么来变更库存信息,mysql数据库直接操作么,又或者是通过java中的原子性类来维护库存信息么
下面就来详细讲讲我们的实践:
<h4>一、整体系统架构</h4>
1、web层,主要是app、h5、online(pc)端的用户流量,目前是7:1:2 流量占比
2、反爬和gateway是公司层面的用于反爬虫以及网关路由
3、restful api主要是用于收集搜索、产品详情、秒杀信息,用于后端跟前端模板的一个映射
4、seckill api主要是用于访问产品的库存信息以及产品的秒杀信息,独立出秒杀api对原有的产品api下单服务不影响,减少原有系统的耦合
5、booking和order api我就不分开来讲,当秒杀成功以后用于生成订单
<h4>二、大胆设计</h4>
关于怎么抗并发有两种策略可以结合,通过前端限流机制只放10%左右的流量到后端,90%的人直接提示秒杀结束,下面我主要是讲讲后端怎么实现。我想redis大家都用过,其抗并发能力超强,理论峰值是单机每秒能支持10万次读写,redis还可以支持分布式集群扩展性强。还有一点redis更新操作是原子性的,更新数据是单线程的安全有保证。锁定库存就用redis来实现,大致的流程如下:
<pre>
</pre>
<b>流程梳理:</b>
1、预热缓存,即将产品信息以及库存信息刷新到缓存之中,难道只存这些信息么?这两项是最主要的,其它附加的后面会讲到
2、后台应用接收前端的访问,我为什么要明确画出tomcat容器,这个后续也是也有用的
3、通过产品信息为key,去对redis库存信息执行库存减操作
4、有个long型值返回
5、判断返回值是否大于或者等于0
6、执行到这里说明该用户应该是可以下订单购买的
7、直接操作insert db ?没错像订单这么重要的信息,还是应该落库为安
8、秒杀商品库存不足,直接返回秒杀结束
<b>缺陷思考</b>
1、在缓存预热好,秒杀开始之前有流量进来怎么办?或者秒杀已经库存为0应该结束秒杀怎么办?为了解决这个问题我们应该在步骤1的时候添加秒杀开始和秒杀结束时间,作为8操作开始之前的第一道判断请求,如果在开始之前以及在结束时间之后直接返回秒杀未开始和秒杀已经结束
所以更全的流程图如下:
<pre>
</pre>
2、操作11中,得知有库存直接下订单会不会真对数据库造成很大压力呢?肯定会的,所以先期减少秒杀的产品数以及库存数量做一些尝试,如果调用订单接口插入订单数据失败,应该释放库存信息,让请求重新可以秒杀产品信息
<b>评估部署多少台应用服务器和redis集群</b>
这个也是可评估的,单台tomcat容器的每秒访问上限是1000左右,单台redis机器低估一下每秒也可以抗1万次读写,举例如果秒杀的每秒的访问是1万,部署10台应用服务器,一个redis集群 2-3台redis服务器(可写的redis节点)理论就都可以抗住了(这边峰值计算容器级别的redis级别需要分开来算,因为先需要访问tomcat应用再去访问redis,tomcat也有连接数上限)。当然一个整个流程下来其实是不需要1秒的,大致100毫秒左右,所以理论抗并发峰值应该是可以抗1万 * 10左右。
<h4>三、风险评估</h4>
<b>a、生产前压力测试</b>
使用jmeter做初期库存数秒杀,看看redis、以及订单接口是否异常或者超时,预估本次活动的访问流量,做上线上的验证
<b>b、生产可降级熔断</b>
1、当瞬间秒杀产品库存太大,造成的redis写暴增,可能造成线程阻塞最后写超时对于如上的异常,添加一个秒杀开关,大量异常时开关关闭停止一切秒杀活动,以免造成更大的损失
2、由于订单还没有做sharding,当每秒写超过单机承受能力甚至影响正常的非秒杀产品下单该怎么办?对于如上的异常添加一个秒杀开关,如果超时或者异常切换到订单临时表中,启用定时任务将订单从临时表数据取出来然后调用下单接口更新到订单库
<h4>四、监控</h4>
1、监控redis调用性能,这边主要是看读和写的性能两个指标,看接口日志是否超时或者异常,查看公司埋点框架mertic统计是否有拐点,因为内存消耗很少通过zabbix查看redis集群cpu信息
2、监控下单接口性能,看是否超时或者异常,统计booking库中失败或者未到order库的订单数,定时预警超过阀值自动发邮件通知相关人员
3、观察订单master数据库的性能,目前master机器是32核128g内存,通过zabbix查看其cpu 内存 以及Io使用情况,查看slave机器的延迟分发情况,因为订单信息显示都是从slave机器读取的
<h4>五、总结</h4>
鉴于目前数据库还不支持sharding,对于大量的insert只能通过增加cpu、内存和SSD来弥补,不利于横向扩展且代价也比较昂贵,后期还是应该完善订单库sharding.降低单点单库的数据更新压力,是个长远的策略.
总之大胆尝试小心验证,多备预案服务做到可切换可降级,减少系统间耦合尽量将风险降到最低。
如果喜欢请帮忙点个赞,谢谢!