NGINX速率限制-限流方案 雨点打透心脏的1/2处 2022-10-22 07:33 211阅读 0赞 **目录** NGINX速率限制如何工作 配置基本速率限制 处理突发 无延迟排队 高级配置示例 白名单 limit\_req在一个位置包含多个指令 配置相关功能 记录中 发送给客户端的错误代码 拒绝所有到特定位置的请求 结论 -------------------- 原文地址:[https://dzone.com/articles/nginx-rate-limiting][https_dzone.com_articles_nginx-rate-limiting] NGINX最有用但经常被误解和配置错误的功能之一是*速率限制*。它允许您限制用户在给定时间段内可以发出的HTTP请求的数量。请求可以像`GET`对网站首页的`POST`请求或登录表单上的请求一样简单。 速率限制可用于安全目的,例如,减慢暴力密码猜测攻击的速度。通过将传入请求速率限制为实际用户的典型值,并(使用日志记录)标识目标URL,它可以帮助[防御DDoS攻击][DDoS]。更一般而言,它用于防止上游应用程序服务器同时被太多用户请求所淹没。 在此博客中,我们将介绍NGINX的速率限制基础以及更高级的配置。速率限制在NGINX Plus中的工作方式相同。 *要了解有关使用NGINX进行速率限制的更多信息,请注册我们[的在线研讨会][Link 1]。* ## NGINX速率限制如何工作 ## ![20210404222226187.png][] NGINX速率限制使用*漏斗算法*,该*算法*广泛用于电信和分组交换计算机网络中,以在带宽受限时处理突发性问题。比方说一个水桶,在水桶的顶部浇水,然后从底部漏水。如果倒水的速度超过漏水的速度,则水桶会溢出。在请求处理方面,水代表来自客户端的请求,存储桶代表队列,根据先进先出(FIFO)调度算法,请求等待处理。泄漏的水表示退出缓冲区以供服务器处理的请求,溢出表示已丢弃且从未得到服务的请求。 ## 配置基本速率限制 ## 速率限制使用两个主要指令`limit_req_zone`和进行配置`limit_req`,如本例所示: limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; server { location /login/ { limit_req zone=mylimit; proxy_pass http://my_upstream; } } 该[`limit_req_zone`][limit_req_zone]指令定义了速率限制的参数,同时在出现的上下文中启用了速率限制(在示例中,针对**/ login /的**所有请求)。 该`limit_req_zone`指令通常在`http`块中定义,使其可在多个上下文中使用。它采用以下三个参数: * **键**\-定义对其应用限制的请求特征。在示例中,它是NGINX变量`$binary_remote_addr`,其中包含客户端IP地址的二进制表示形式。这意味着我们将每个唯一的IP地址限制为第三个参数定义的请求速率(我们使用此变量,因为它占用的空间少于客户端IP地址的字符串表示形式`$remote_addr`)。 * **区域**\-定义用于存储每个IP地址状态及其访问请求限制URL的频率的共享内存区域。将信息保存在共享内存中意味着可以在NGINX工作进程之间共享信息。该定义分为两部分:由`zone=`关键字标识的区域名称,以及冒号后面的大小。大约16,000个IP地址的状态信息需要1兆字节,因此我们的区域可以存储大约160,000个地址。如果NGINX需要添加新条目时存储空间已用完,它将删除最旧的条目。如果释放的空间仍然不足以容纳新记录,则NGINX返回状态代码 `503(TemporarilyUnavailable)`。另外,为防止内存耗尽,NGINX每次创建一个新条目时,都会删除最多60秒钟内未使用的两个条目。 * **费率**\-设置最大请求率。在该示例中,速率不能超过每秒10个请求。NGINX实际上以毫秒粒度跟踪请求,因此此限制对应于每100毫秒1个请求。因为我们不允许突发,这意味着如果请求在前一个允许的请求之后不到100毫秒到达,则该请求将被拒绝。 该`limit_req_zone`指令设置速率限制和共享内存区域的参数,但实际上并没有限制请求速率。为此,您需要通过在其中包含指令来将限制应用于特定`location`或`server`块`limit_req`。在示例中,我们将对**/ login /的**请求进行速率限制。 因此,现在每个唯一IP地址每秒只能对**/ login /**请求10个请求,或者更确切地说,不能在上一个IP地址的100毫秒内对该URL发出请求。 ## 处理突发 ## 如果我们彼此之间在100毫秒之内收到2个请求怎么办?对于第二个请求,NGINX将状态代码返回`503`给客户端。这可能不是我们想要的,因为应用程序本质上往往是突发性的。相反,我们希望缓冲任何多余的请求并及时为它们提供服务。在此,我们在此`burst`参数中使用参数`limit_req`,如在此更新的配置中: location /login/ { limit_req zone=mylimit burst=20; proxy_pass http://my_upstream; } 该`burst`参数定义了一个客户端可以发出的请求超出该区域指定的速率(对于我们的示例**mylimit**区域,该速率限制是每秒10个请求,或每100毫秒1个)。一个请求比上一个请求晚100毫秒后到达,在这里,我们将队列大小设置为20。 这意味着,如果同时有21个请求从给定IP地址到达,NGINX将第一个请求立即转发到上游服务器组,并将其余20个请求放入队列。然后,它每100毫秒转发一个排队的请求,并`503`仅在传入请求使排队的请求数超过20时才返回到客户端。 ## 无延迟排队 ## 具有的配置可`burst`带来顺畅的流量,但不是很实用,因为它可能会使您的站点显得很慢。在我们的示例中,队列中的第20个数据包等待2秒被转发,这时对它的响应可能不再对客户端有用。要解决这种情况,请将`nodelay`参数与参数一起添加`burst`: location /login/ { limit_req zone=mylimit burst=20 nodelay; proxy_pass http://my_upstream; } 使用该`nodelay`参数,NGINX仍会根据该`burst`参数在队列中分配时隙,并强加配置的速率限制,但不会通过间隔排队请求的转发来实现。相反,当请求“过早”到达时,只要队列中有可用的插槽,NGINX就会立即转发该请求。它将该插槽标记为“已占用”,并且直到经过适当的时间(在我们的示例中为100毫秒)后,它才会释放该插槽供其他请求使用。 像以前一样,假定20插槽队列为空,并且从给定IP地址同时到达21个请求。NGINX立即转发所有21个请求,并将队列中的20个时隙标记为已占用,然后每100毫秒释放1个时隙(如果存在25个请求,NGINX将立即转发其中的21个请求,将20个时隙标记为已接收并拒绝4个请求状态`503`)。 现在假设在转发第一组请求后的101毫秒内,又有20个请求同时到达。队列中只有1个插槽已被释放,因此NGINX转发1个请求,并拒绝其他19个状态为status的请求`503`。相反,如果在20个新请求到达之前已经过去501毫秒,则5个时隙是空闲的,因此NGINX立即转发5个请求并拒绝15个。 该效果相当于每秒10个请求的速率限制。`nodelay`如果您想施加速率限制而不限制请求之间允许的间隔,则此选项很有用。 **注意:**对于大多数部署,我们建议在指令中包含`burst`和`nodelay`参数`limit_req`。 ## 高级配置示例 ## 通过将基本速率限制与其他NGINX功能结合使用,您可以实现更细微的流量限制。 ### 白名单 ### 此示例说明了如何对来自不在“白名单”上的任何人的请求施加速率限制。 geo $limit { default 1; 10.0.0.0/8 0; 192.168.0.0/24 0; } map $limit $limit_key { 0 ""; 1 $binary_remote_addr; } limit_req_zone $limit_key zone=req_zone:10m rate=5r/s; server { location / { limit_req zone=req_zone burst=10 nodelay; # ... } } 本示例同时使用 `geo` 和 `map` 指令。该`geo`块为白名单中的IP地址和所有其他IP地址分配一个`0`to值。然后,我们使用映射将这些值转换为键,例如:`$limit``1` * 如果`$limit`为`0`,`$limit_key`则设置为空字符串。 * 如果`$limit`为`1`,`$limit_key`则以二进制格式设置为客户端的IP地址。 将两者放在一起,`$limit_key`将白名单的IP地址设置为空字符串,否则将其设置为客户端的IP地址。当`limit_req_zone`目录的第一个参数(键)为空字符串时,不会应用该限制,因此白名单的IP地址(在10.0.0.0/8和192.168.0.0/24子网中)不受限制。所有其他IP地址限制为每秒5个请求。 该`limit_req`指令将限制应用于该 `/` 位置,并允许超过配置的限制的最多10个数据包的突发,转发没有延迟 ### `limit_req`在一个位置包含多个指令 ### 您可以`limit_req`在一个位置包含多个指令。将应用与给定请求匹配的所有限制,这意味着将使用限制性最强的限制。例如,如果一个指令强加了一个延迟,则使用最长的延迟。类似地,即使这是任何指令的结果,请求也会被拒绝,即使其他指令允许它们通过。 扩展前面的示例,我们可以对白名单上的IP地址应用速率限制: http { # ... limit_req_zone $limit_key zone=req_zone:10m rate=5r/s; limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s; server { # ... location / { limit_req zone=req_zone burst=10 nodelay; limit_req zone=req_zone_wl burst=20 nodelay; # ... } } } 白名单上的IP地址不匹配第一个速率限制(**req\_zone**),但是匹配第二个速率限制(**req\_zone\_wl**),因此每秒限制为15个请求。不在白名单上的IP地址同时符合两个速率限制,因此适用的限制更为严格:每秒5个请求。 ## 配置相关功能 ## ### 记录中 ### 默认情况下,NGINX记录由于速率限制而延迟或丢弃的请求,如以下示例所示: -------------------- 2015/06/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2 日志条目中的字段包括: * `limitingrequests` -指示日志条目记录了速率限制的指示符。 * `excess` -超出此请求代表的配置速率的每毫秒请求数。 * `zone` -定义强制速率限制的区域。 * `client` -发出请求的客户端的IP地址。 * `server` -服务器的IP地址或主机名。 * `request` -客户端发出的实际HTTP请求。 * `host``Host`\-HTTP标头的值。 默认情况下,NGINX在该`error`级别上记录拒绝的请求,如上`[error]`例所示(它在较低级别上记录延迟的请求,因此`info`默认情况下)。要更改日志记录级别,请使用[`limit_req_log_level`][limit_req_log_level]伪指令。在这里,我们将拒绝的请求设置为登录`warn`级别: location /login/ { limit_req zone=mylimit burst=20 nodelay; limit_req_log_level warn; proxy_pass http://my_upstream; } ### 发送给客户端的错误代码 ### 默认情况下,`503`当客户端超出其速率限制时,NGINX会以状态代码响应。使用[`limit_req_status`][limit_req_status]指令设置不同的状态代码(`444`在此示例中): location /login/ { limit_req zone=mylimit burst=20 nodelay; limit_req_status 444; } ### 拒绝所有到特定位置的请求 ### 如果要拒绝对特定URL的所有请求,而不仅仅是限制它们,请为其配置一个块并包含`all`指令: location /foo.php { deny all; } ## 结论 ## 我们已经介绍了NGINX和NGINX Plus提供的速率限制的许多功能,包括为HTTP请求上的不同位置设置请求速率,以及配置其他功能以限制速率,例如`burst`和`nodelay`参数。我们还介绍了高级配置,这些配置为白名单和黑名单的客户端IP地址应用了不同的限制,并说明了如何记录拒绝和延迟的请求。 [https_dzone.com_articles_nginx-rate-limiting]: https://dzone.com/articles/nginx-rate-limiting [DDoS]: https://www.nginx.com/blog/mitigating-ddos-attacks-with-nginx-and-nginx-plus/ [Link 1]: https://www.nginx.com/resources/webinars/rate-limiting-nginx/ [20210404222226187.png]: /images/20221022/05061e327d734a8caf624308f8339416.png [limit_req_zone]: http://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_zone [limit_req_log_level]: http://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_log_level [limit_req_status]: http://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_status
还没有评论,来说两句吧...