漏桶算法和 NGINX 的 limit_req 模块

漏桶算法和 NGINX 的 limit_req 模块

Chris Yue No Comment
Posts

DDOS 攻击可能是网络上最常见的攻击之一。互联网发展到今天,当然也有很多成熟的抵御 DDOS 攻击的方法。今天的主角跟其有密切关系,他被称之为漏桶算法。

漏桶算法要解决的一个核心问题,便是如何将不断变化的访问量,变成相对稳定的一个访问量。这么说可能太过抽象,举一个实际的例子,NGINX 接收到互联网上的请求,速率是 n requests/second,或者说 n RPS,但这个 n 是不可控制的,可能是 0.1,也可能是 10 万。NGINX 需要将这些请求发送给后台的 PHP-FPM 服务去处理。假如被访问的 PHP 脚本,能稳定处理的 RPS 是 1 万,而实际的访问量正不断在 0.1 RPS 和 10 万 RPS 之间徘徊(这个例子有点太夸张,懂我意思就行),那么有什么办法可以让 PHP-FPM 接受到来自 NGINX 的请求能相对稳定,这就是漏桶算法需要解决的问题。

漏桶这个名字,其实就非常形象的描述了算法本身的原理。大家都知道,一个身上打了 n 个眼儿的桶,无论你倒进桶里的水多还是少,漏出来的水的流速也会保持稳定,这就是此算法的本质。再以 NGINX + PHP-FPM 为例,我们在 NGINX 配置里定义一个最大处理请求的速度,如果 PHP-FPM 的稳定处理速度峰值是 1000 RPS,那就在 NGINX 里定义处理请求速度最大为 1000 RPS。当 RPS 已经大于这个值的时候,多出来的请求就被 NGINX 这个桶暂时储存起来,排着队等待处理。在 NGINX 的精心照料下,PHP-FPM 会相对稳定的处理来自 NGINX 的请求,而不会出现突然暴增的请求让 PHP-FPM 处理不过来,甚至挂掉。

然而桶也有大小,NGINX 也一样,假如请求太多太多,桶都装不下了,那么桶将会把多出来的请求直接漏掉,返回 503 错误。

上面是服务器的故事,而用户那边的故事是:如果用户浏览器发起请求时,服务器的 RPS 还在 1000 以内,那就看不出什么变化;但如果发起请求的时候正好赶上访问高峰期,那么浏览器会一直等待响应,直到请求“漏”给了 PHP-FPM 处理;极端情况,如果赶上访问多得服务器无法承受时,用户就只能得到一个 503 错误。

精心呵护 PHP-FPM 避免出现因压力突然增大而导致的雪崩效应,是漏桶的一种用法。然而,没有网站愿意返回 503 错误信息给用户。为了避免 503,更正确的姿势是使用负载均衡,增加网站的横向扩展能力以扛住压力,当然,这是另外一个话题了。不过有一种访问,叫恶意访问,以不让服务器罢工不罢休为目的,这种恶意访问叫 DDOS(Distributed Denial of Service),而漏桶更多是用在防止 DDOS 攻击上。

大部分 DDOS,都属于低成本 DDOS 攻击,也就是通过一台或者几台机器,不断向服务器发起请求来实现的。我们只要给每一个 IP 设置一个“桶”,并做一个限制:每个 IP 一秒最多只能有 10 次请求,否则视为有问题的请求。在 NGINX 的配置上体现为:10 RPS,桶的大小为 0,如果一秒内有超过 10 个请求被处理,多出来的请求会立马返回 503。当然这种处理方式也是略显粗暴,配置不好会误伤小区宽带内的正常访问。

说清楚了原理,具体配置的例子如下,其中 rate 就是上面所说的 RPS,而且不仅可以设置 n r/s,也可以设置 n r/m;另外 burst 参数,就是我们的桶大小了:

配置里的一些细节,这里就不多做介绍了,这些在官方的文档里面都能查得到:

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

2016-04-22 补充:上面说到访问频率可以设置 n r/s,或者 n r/m,可能有些人会有疑问,1 r/s 和 60 r/m 有区别吗?是否 60 r/m 意味着可以在一分钟的第一秒的时候就有 60 个正常的返回,根据我的测试,其实就是一样的效果,无论访问频率的单位是什么,最终应该都算成了“一次请求的占用时长”

漏桶算法和 NGINX 的 limit_req 模块 by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

微信赞赏码

文章不错,我要帮站长分担建站费!
天使投赏人

发表评论

− 4 = 1