首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

预案三板斧的限流大法

限流策略:多维防御+纵深防御

限流能力

限流是针对请求的各种特征,多维防御+纵深防御,从而限制流量,实现对服务端资源的合理使用。这里的特征是指一个请求所包含的各种信息,包括但不限于IP、Header、URI、Cookie等。常见的限流策略有以下三种(以Nginx为例进行说明):

  • 限制请求数,意思是请求的次数不能太多

Nginx:http://nginx.org/en/docs/http/ngx_http_limit_req_module.html

  • 限制并发连接数,意思是请求的频率不能太快

Nginx:http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html

  • 限制传输速度,意思是请求的体积不能太大

Nginx:http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate

Nginx还提供work_connections和worker_processes这类针对程序的全局设置,但是这些配置并非是客户端的请求的特征,不能直接作用于特定请求特征的流量,因此不属于本文限流的讨论范畴。

多维防御

所谓的多维防御,不仅仅针对IP进行限制,还可以从URI、Cookie、状态码等多个维度进行限制,同时还可以组合起来进行精细化的限制,举例说明:

  • 状态码+IP,如果恶意扫描整个网站,必然会出现较多的404状态码,这时候结合其他维度,就可以进行一定的防御和限制
  • URI+Cookie,可以避免公司IP出口导致的请求被误伤,同时也解决了IP模式下阈值设置太大的问题

纵深防御

所谓的纵深防御,则是指针对单一维度,进行多层级的防御,实现精细化的限制。以IP维度为例进行说明:

  • 每秒钟,单个IP最多可以访问100次
  • 每十秒钟,单个IP最多可以访问500次
  • 每分钟,单个IP最多可以访问1000次

阈值设置

上述的阈值只是简单举例,并没有太大的参考价值,在实际部署的过程中,一般需要至少30天的访问日志以及多个节假日的访问日志进行离线分析,从而得出业务特定的合理阈值,然后在通过灰度逐步全量。初期的阈值,建议设置的比预估阈值至少高一倍,先具备限流功能,然后再慢慢打磨阈值,避免因为阈值设置不合理,导致限流功能上线受阻,那就得不偿失了。

限流的算法

那各种限流策略背后的实现机制是怎样的呢?常见的限流算法有固定窗口计数器,滑动窗口计数器,漏洞和令牌桶四种,在各类软件的具体实现上却不尽相同,详细内容可以参考这篇文章《分布式系统关注点——限流该怎么做?》,本文仅介绍较为常用的两种算法。

固定窗口计数器

将时间划分为固定时间窗口(例如每秒一个单位),在每个固定的时间窗口内对请求进行计数,如果请求的次数超过了限制,则本时间窗口内所有的后续请求都被丢弃,直到下一个固定时间窗口时,重置计数器。

该算法常被吐槽流量突增,举例来说100r/m(每分钟100次请求),可能在第一秒钟就请求完毕,更有甚者,在两个时间窗口交界处可能会产生2倍的请求,这样就会导致短时的流量突增。但是,只要把时间粒度控制在秒级,效果还是可以的。因此,滑动窗口就没有太大的必要进行介绍了,大家看相关文章即可。

漏桶

漏桶算法,有一个固定大小的队列,进入的请求全部放入该队列中,如果队列满,则丢弃进入的请求。同时,以固定的速率从队列中消费请求。好处就是,不管流量进来多少,出去的速率始终固定。

该算法的主要不足就是消费速率设置会偏保守一些,因为不同请求的资源消耗和处理耗时不同,为了能够适配各类场景,必然会导致消费速率设置较为保守。缓解的方法,对不同的请求类型进行集群拆分,从而能够更加精细化的设置消费速率。

各层级限流

全局统一接入:GFE/BFE/JFE

全局统一接入的产品,Goolge叫做GFE,Baidu叫做BFE,JD叫做JFE。这类产品会将公司所有对外流量进行统一的接入和调度,同时具备较强的通用防护能力,如抗D、WAF等。

对于一些业务定制化的复杂限流规则,一般会下放到各个业务线自己的接入端来实现。

Webserver:Nginx/OpenResty

上面介绍各种XFE的时候提到,一些业务定制化的复杂限流规则,会下放到各个业务线的接入端来实现,在这个阶段,因为Webserver的选择较多,所以实现方式也较多,没有统一的标准,一般是直接利用webserver提供的限流能力,或者是通过插件方式来实现,前者多以Nginx为主,后者则是Nginx+lua/Nginx+lua+redis的形式。

本文以Nginx为例来进行说明,业务侧的整体思路应该是多维防御+纵深防御,从而实现较好的限流效果。

多维防御,以Nginx的limit_req为例进行说明,不仅仅对来源IP进行流控,还可以对请求的uri,cookie进行流控,还可以对ip+uri+cookie的组合进行流控,从多个维度下手,进而实现更好的效果。

limit_req_zone $ip zone=1:10m rate=100r/s;

limit_req_zone $uri$ip zone=2:10m rate=100r/s;

limit_req_zone $uri$ip$cookie zone=3:10m rate=100r/s;

limit_req zone=1 burst=30 nodelay;

limit_req zone=2 burst=10 nodelay;

limit_req zone=3 burst=30 nodelay;

纵深防御,以Nginx的limit_req为例进行说明,主要是通过漏斗思路,设置三级防御体系,第一级是粗滤,因此阈值可以设置的较大,仅拦截明显的恶意行为即可,第二级过滤,可以拦截一些异常请求,第三级过滤,则可以将大部分异常请求进行拦截。

limit_req_zone $ip zone=11:10m rate=100r/s;

limit_req_zone $ip zone=12:10m rate=500r/10s;

limit_req_zone $ip zone=13:10m rate=1200r/1m;

limit_req zone=1 burst=50 nodelay;

limit_req zone=2 burst=100 nodelay;

limit_req zone=3 burst=200 nodelay;

备注:Nginx本身并不支持500r/10s这种模式,同时,60r/m的效果等同于1r/s,因此上述的配置只能起到一个演示的作用。实际上是需要大家通过插件的方式来实现上述的效果。Nginx官方对于r/m的解释如下:The rate is specified in requests per second (r/s). If a rate of less than one request per second is desired, it is specified in request per minute (r/m). For example, half-request per second is 30r/m.

应用程序和中间件:Istio

对于大部分自研的程序来讲,限流功能如有必要,就得自行实现了,算法前面已经介绍过了,当然,也可以参考Google的RateLimiter。

对于进入了Docker的程序,则可以考虑引入Istio,改造成本可控,收益上,限流,熔断,鉴权等等功能均可具备,但是目前大规模应用的公司还不多,大家都是在慢慢探索中。

对于短期内还无法实现虚拟化的业务,则可以考虑类似Istio的模式,在每个程序前,在关键程序前,在和多个下游交互的程序前,适度放置Proxy来实现流控的效果。在AWS的Proxy-ELB,就是挂在了每组服务之前。这种模式的缺点就在于会略为增加延时,但如果是同Regin部署,那么只要增加的Proxy层级数量可控,整体延时影响也可控。

极少数情况下,也有业务直接通过Iptable的方式进行限流,对于物理机的部署模式,笔者只能建议要谨慎使用。

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/L1FThcLIgzHSYlIaDk0R
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券