高并发系统中的限流与降级

在设计云端高并发系统中,除常见的服务发现、弹性扩容、全链路监控之外,需要注意的就是系统的高并发注量保护及可用性保证,一般常用的措施有4种:

1、限流,是指需要限制并发/请求量的场景(如秒杀等)。

2、降级,是指当服务暂时不可用或者影响到核心流程时,需要待高峰或者问题解决后再打开。

3、缓存,本次不介绍

4、熔断,本文不介绍,后面专起一篇介绍

一、限流

云端系统在设计之初,会针对上线使用情况进行容量预估,若实际流量超过系统所能承受的TPS/QPS阈值,系统可能会被压垮,最终导致整个服务不可用。为了避免这种情况,我们就需要对接口请求进行限流。在实际业务中,一般的业务服务器都部署在反向代理服务器(Ngnix)后面,一般情况下,Ngnix不会成为瓶颈,后端的应用服务器会由于各种原因(IO等待时间长、数据库慢、代码自身的缺陷等)成为瓶颈,所以主要是根据应用服务器的负载情况来计算一个阀值,如果超过这个值,则拒绝新的请求

限流的目的是平缓瞬时高峰流量而对并发访问请求进行限速,或者在特定时间窗口内的请求数量进行限制来保护系统,一旦达到限制速率则可以拒绝服务或排队等待。

常见的限流模式有控制并发和控制速率,一个是限制单位时间窗口内的请求数量,一个是限制并发访问的速率。

1、控制并发数量

1)在Nginx侧,限流方案有很多种,可以限制连接数limit_conn,可以限制访问频次limit_req,可以启用过载保护sysguard模块。可以对某个url进行限流,也可以对访问IP进行限流。

limit_zone,是针对每个变量(这里指IP,即$binary_remote_addr)定义一个存储session状态的容器。若定义一个20m的容器,按照32bytes/session,可以处理640000个session。

limit_conn one 10 : 表示一个IP能发起10个并发连接数。

limit_req: 与limit_req_zone对应。burst表示缓存住的请求数。

limit_req_zone 与limit_zone类似。rate是请求频率. 每秒允许12个请求。

2)在业务侧,一般可以通过信号量、或是控制线程池大小来实现。

如:

2、控制访问速率

一般有两种方法,令牌桶和漏桶算法,常用令牌桶算法来实现,

1)漏桶算法:用于控制从本节点发出请求的速率

2)令牌桶算法:用于控制从外界发送到本节点的请求的速率

针对用户洪峰更适合令牌桶算法。

在widipedia上,令牌桶算法是这么描述的:

1)每过1/r秒桶吕增加一个令牌

2)桶中最多存放b个令牌,如果桶满了,消耗n个令牌,然后发送该数据包

3)当一个n字节的数据包到达时,消耗n个令牌,然后发送该数据包

4)如果桶中可用令牌小于n,则该数据包将被缓存或丢弃

令牌桶的主要作用是控制 QPS和TPS,以一个恒定的速度往桶中放入令牌,而如果请求需要被处理,则需要先从桶中获取一个令牌,当桶里没有令牌可取时,则拒绝服务;令牌桶的另一个好处是可以改变速率。

在实践中,通用使用Guava的RateLimiter来实现速率控制 ,或者通过计算每秒钟的请求调用次数,来判断并限制请求。

在实际工作中,如果无差别拒绝请求,会影响用户体验,所以要配合一定的策略:

1)通过防刷算法,拒绝疑似攻击的请求。通过用户识别码、IP、所在区域、上网特征等方式拒绝频繁访问的用户。

2)尽量不影响已服务用户的使用体验。可以给这些用户一个令牌,记录用户一些重要操作时间,在流量不够的情况下,优先放行具备访问令牌的用户。

3)尽量保证会员的体验

二、降级

当访问量剧增、服务出现问题(如响应慢或不响应时)或非核心业务服务影响到核心业务的性能时,要保证服务还是可用的,哪怕是有损服务,系统可以设置一些参数进行自动降级,也可以配置开关实现人工降级。

降级的目标是保证核心服务是可用的,即使是有损的,但诸如购物车、结算等服务不建议降级。

1、降级预案

1)服务请求超时,可以针对某些接口进行降级

2)一段时间内的成功率有波动,可以发送告警,并对某些接口进行降级

3)成功率大大降级、或者数据库连接池被打爆,或是访问量超过阈值,可以进行限流降级

4)数据出现大量错误

5)页面降级,在大促或某些情况下,某些页面占用了一些稀缺服务资源,在紧急情况下对其整个降级,以达到丢卒保车

6)页面片段降级,比如商品详情中的商家部分出现数据错误,需要对其降级

7)页面异步请求降级,比如有推荐信息或配送等异步请求,如果这些信息响应慢或后端服务出错,可以进行降级

8)服务功能降级,比如渲染商品详情页需要调用五些不太重要的服务:相关分类、热销榜等,而这些服务在异常情况下直接不获取,即降级即可

9)读降级,比如多级缓存模式,如果后端服务有问题,可以降级为只读缓存,这种方式适用于对读一致性要求不高的场景。

10)写缓存,比如秒杀抢购,我们可以只进行cache的更新,然后异步同步到DB,最终保证一致性即可,此时可以将DB降级为cache

11)爬虫降级,在大促活动时,可以将爬虫流量导向静态页或者返回空数据从而降级保护后端稀缺资源

2、自动开关降级

自动降级是指根据系统负载、资源使用情况、SLA等指标进行降级

1)超时降级

当访问的数据/http服务/rpc远程调用响应慢或者长时间响应慢,且该服务不是核心服务的话,可以在超时后自动降级;比如商品详情页上有推荐内容或评价,但是推荐内容/评价暂时不展示不会产生很大的影响;对于这种服务是可以降级的。如果调用别人的远程服务,和对方定义一个服务响应最大时间,如果超时了则自动降级

2)统计失败次数降级

有时候某些API会存在一些不稳定的情况,当失败调用次数达到一定阈值时自动降级;然后通过异步线程去探测服务是否恢复了,则取消降级。

3)故障降级

比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、RPC服务抛出异常),则可以直接降级。降级后的处理方案有:默认值、兜底数据、缓存

4)限流降级

当秒杀和抢购活动时,如果瞬时访问量太大而导致系统崩溃,此时可以考虑使用限流来进行访问量限制,当达到流量阀值时,后续请求会被降级,如排队页面、错误页面等

3、人工开关降级

在大促期间通过监控发现线上的一些服务存在问题,这个时候需要暂时将这些服务摘掉;还有时候通过任务系统调用了一些服务,但是服务依赖的数据库可能存在问题(连接被打满、挂掉、响应慢等),此时需要暂停下任务系统让服务方进行处理;还有发现突然调用量太大,可能需要改变处理方式(如同步转异步);此时就可以使用开关来完成降级。开关可以通过动态下发。

在线上进行灰度测试,但是不太确定该服务是否正确,就需要设置开关,当服务有问题时可以通过开关切换回老服务。

4、读服务降级

读服务的降级一般采用的策略有:暂时切换读(降级到读缓存、降级到走静态化)、暂时屏蔽读(屏蔽读入口、屏蔽某个读服务)。读服务的流程:即接入层缓存->应用层本地缓存->分布式缓存->DB/RPC服务,我们会在接入层、应用层设置开关,当分布式缓存、RPC服务/DB有问题自动降级为不调用。

页面降级、页面片段降级、页面异步请求降级都是读服务降级,目的是丢卒保帅(比如因为这些服务也要使用核心资源、或者占了带宽影响到核心服务)或者因数据问题暂时屏蔽。

还有一种是页面静态化场景:

动态化降级为静态化,比如大促活动时,某些页面走动态化渲染的,可以切换为静态化,比如列表页、首页、频道页都可以这么做,可以通过一个程序定期推送静态页到缓存或者磁盘,出问题时直接切过去。

5、写服务降级

写服务在大多数场景下是不可降级的,不过可以通过一些方法来解决问题,比如同步转异步,限制写操作等,比如扣减库存一般这样操作:

方案一:

1)扣减DB库存

2)扣减成功后更新Redis

方案二:

1)扣减redis

2)同步扣减DB,如果扣减失败则回滚redis

方案三:

1)扣减redis

2)正常同步扣减db,性能扛不住时降级异步进行DB库存扣减,最终实现一致即可

方案四:

1)扣减redis

2)正常同步扣减db,性能扛不住时降级为写扣减DB库存消息到本机,然后待压力下降时,再异步给DB

也就是说正常情况下可以同步扣减库存,性能压力较大时降级为异步;另外如果是秒杀场景可以直接降级为异步,从而保护系统;在大促高峰时,可以将操作数据写入redis,等高峰过去后,再同步回DB。

比如用户评价,可以让一部用户看不到评价按钮。

6、多级降级

缓存是离用户越近越高效;而降级是离用户越近对系统保护越有效,因为业务的复杂性导致越到后端QPS/TPS越低。

1)页面JS降级开关

主要控制页面功能的降级,在页面中通过js脚本部署功能降级开关,适当时机打开

2)接入层降级开关

一般是网关,主要控制请求入口的降级、请求进入后会首先进入接入层,在接入层可以配置功能降级开关,可以根据实际情况进行自动/人工降级;

3)应用层降级开关

主要控制业务的降级,在应用中配置相应的功能开关,根据实际情况进行自动/人工降级。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180416G15PG100?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券