背景
国庆前几天,我们产品说,营销那边有个秒杀演唱会门票活动,大概峰值QPS在15k左右,然后放到我们这边来做。我们这边的活动,日常qps大概在20-30之间,且设计之初,就没有考虑过秒杀抢票活动。所以,得做不少改动。
我们这边整个流程分为售卖页、填写页、下单三个接口。其中流量是一个漏斗型,流量大小依次是:售卖页 - > 填写页 - > 下单
1、调研
1.1、预估相关页面/接口大致的流量
首先,我们需要预估一个最开始的入口的流量。比如我们一开始预估的售卖页的QPS在15k左右。然后我们可以统计下正常情况下的转化率。比如,正常的活动,我们的售卖页的一天的流量为x
填写页一天的流量为y
下单的一天的流量为z
那么,填写页的流量预计为x/y * 15k
下单的流量为x/z*15k
这里仅作为一个参考
1.1、统计相关被调用方
首先,需要整理各个页面调用了什么接口,然后各个接口有没有相关的依赖(这个依赖可以是其它接口,也可以是db),然后各个依赖的情况。比如我们的售卖页只有个产品售卖信息接口,但是涉及的调用的接口有四个。当我们这边有很大流量时,需要考虑相关的被调用方是否也能够承担该流量下的压力。
我们需要整理一个文档或者Excel,将相关的接口、依赖等罗列出来,并详细的填写被依赖方能支持的qps。
2、方案
2.1、简化流程
在明确我们这边的被调用方之后,我们需要进行评估:
- 1、该依赖是否是必须的,能否通过其它方式减少依赖;
这一步我们可以通过调整校验步骤等进行调整,如,库存校验放在第一步,如无库存,是否可以直接展示已售罄的默认页面。 - 2、该依赖一旦崩溃,会造成什么影响,是否有兜底的方案;
我们需要做好被依赖方崩溃的准备,如产品信息接口/db连接超时或者返回异常,我们是否有相应的异常处理。 - 3、是否可以将部分静态数据或者库存限制等,加入缓存;
比如我们可以将产品信息,提前写入缓存。一般来说,产品的一些基础数据是不会变化的,或者说很少发生变化(至少在秒杀的这段时间内,基本不会变化),这种数据,我们甚至可以写入本地缓存,而非redis(比较读取redis本身也是会有网络开销的,且redis本身也需要支持对应的qps)。
在我们售卖页,需要调用的4个接口中,其中一个是校验可售接口,正常是去异步调用的,在这个时候,我们可以将其提前,并且将无库存结果缓存到redis中。这样,大部分用户的请求并不会打到其它三个依赖中,从而减小相关依赖的压力。
2.2、限流
从一开始,我们就说我们的流量模型是漏斗型,且流程往下,我们的接口能接受的压力也越小(如下单接口,由于接口中带有多个校验逻辑,因此该接口本身所能承载的压力上限就不会高),因此,我们在售卖页优先限流,在同一时间,仅允许x个人(该变量是可配置的)进入售卖页。在控制同一时间仅允许x个人进入页面时,有以下几个问题需要考虑:
1、当用户离开售卖页时,如何识别;
2、用户应该可重入,即刷新不应该被限流;
第一个问题,我们是当用户进入填写页时,将其移除售卖页白名单列表,同时,在将其加入白名单时,本身是有一个超时时间的,防止用户一直停留在售卖页却占用资源;第二个问题,我们把用户的cid作为key,这样用户刷新时,cid是不变的,也就实现可重入。
压测
总结
在做秒杀活动时,我们需要充分考虑到全流程是否能支持秒杀活动,我们的依赖方是否支持我们的这个流量级别。由于秒杀对性能有所要求,因此,我们针对活动中每一个被依赖方,都进行仔细考虑:我们能否去除这个依赖?对于一些静态数据的依赖,我们可以暂时将其写入缓存(可以是本地缓存)以减少依赖项,提升性能。