流量削峰
需求
当系统发布后,系统在一定时间内总会有一个流量高峰,尤其是秒杀系统,在秒杀开始的那一秒是一条很直很直的线,这是因为秒杀请求在时间上高度集中于某一特定的时间点。一个特别高的流量峰值对资源的消耗是瞬时的。
为什么削峰
服务器处理资源能力是恒定的,用或不用都是一样的,所以出现峰值会导致处理不过来,峰值过后又会闲下来。我们要保证服务质量,一般会按照峰值时计算,比如最大在线人数等指标。
削峰的目的:
一、可以让服务端处理变得更加平稳。
二、可以节省服务器的资源成本。
削峰的本质是更多地延缓用户请求的发出,以便减少和过滤掉一些无效请求。
排队
消息队列方案
用消息队列来缓冲瞬时流量,把同步的直接调用转换成异步的间接推送,中间通过一个队列在一端承接瞬时的流量洪峰,在另一端平滑地将消息推送出去。
但是,如果流量峰值持续一段时间达到了消息队列的处理上限,例如本机的消息积压达到了存储空间的上限,消息队列同样也会被压垮,这样虽然保护了下游的系统,但是和直接把请求丢弃也没多大的区别。
线程池加锁等待
内存排队算法
先进先出、先进后出等常用的内存排队算法的实现方式
请求序列化到文件
把请求序列化到文件中,然后再顺序地读文件(例如基于 MySQL binlog 的同步机制)来恢复请求等方式。
答题
主要是为了增加购买的复杂度,从而达到两个目的。
第一个目的是防止部分买家使用秒杀器在参加秒杀时作弊。
第二个目的其实就是延缓请求,起到对请求流量进行削峰的作用,从而让系统能够更好地支持瞬时的流量高峰。这个重要的功能就是把峰值的下单请求拉长,从以前的 1s 之内延长到 2s~10s。这样一来,请求峰值基于时间分片了。这个时间的分片对服务端处理并发非常重要,会大大减轻压力。而且,由于请求具有先后顺序,靠后的请求到来时自然也就没有库存了,因此根本到不了最后的下单步骤,所以真正的并发写就非常有限了。这种设计思路目前用得非常普遍,如当年支付宝的“咻一咻”、微信的“摇一摇”都是类似的方式。
分层过滤
对请求进行分层过滤,从而过滤掉一些无效的请求。
分层过滤其实就是采用“漏斗”式设计来处理请求的。
假如请求分别经过 CDN、前台读系统(如商品详情系统)、后台系统(如交易系统)和数据库这几层,那么:
大部分数据和流量在用户浏览器或者 CDN 上获取,这一层可以拦截大部分数据的读取;
经过第二层(即前台系统)时数据(包括强一致性的数据)尽量得走 Cache,过滤一些无效的请求;
再到第三层后台系统,主要做数据的二次检验,对系统做好保护和限流,这样数据量和请求就进一步减少;
最后在数据层完成数据的强一致性校验。
这样就像漏斗一样,尽量把数据量和请求量一层一层地过滤和减少了。
分层过滤的核心思想是:在不同的层次尽可能地过滤掉无效请求,让“漏斗”最末端的才是有效请求。
分层校验的基本原则是:
1.将动态请求的读数据缓存(Cache)在 Web 端,过滤掉无效的数据读;
2.对读数据不做强一致性校验,减少因为一致性校验产生瓶颈的问题;
3.对写数据进行基于时间的合理分片,过滤掉过期的失效请求;
4.对写请求做限流保护,将超出系统承载能力的请求过滤掉;
5.对写数据进行强一致性校验,只保留最后有效的数据。
客户端层面做一些请求的随机丢弃,这些被丢弃的请求就直接返回失败,或者系统繁忙,让用户重试。过滤掉一部分流量向服务端发送。
Nginx不仅可以用来做负载均衡和流量的分发,其实他也是可以做流量的过滤的,这里面可以配置一些黑白名单、可以通过IP进行限流、也可以做一些业务校验都是可以的。
在读系统中,尽量减少由于一致性校验带来的系统瓶颈,但是尽量将不影响性能的检查条件提前,如用户是否具有秒杀资格、商品状态是否正常、用户答题是否正确、秒杀是否已经结束、是否非法请求、营销等价物是否充足等;
在写数据系统中,主要对写的数据(如“库存”)做一致性检查;使用sentinel做限流熔断系统保护。
最后在数据库层保证数据的最终准确性(如“库存”不能减为负数)。