Nginx限速是怎样工作的

Nginx限速使用 Leaky(唝水桶)算法,比喻为水桶顶部倒水,底部漏水,如果倒入水的速率超过漏水的速度,则水桶漏出。在电信网络和分组交换网络中,带宽有限的情况下该算法使用场景较多。

Nginx 服务器之速率限制-LMLPHP

就请求处理而言,水代表客户端的请求,存水的桶按先进先出(FIFO)调度算法处理的队列。漏出的水表示退出缓冲区等服务器处理,而溢出表示请被丢弃且不再提供服务。

配置基本的速率限制

速率限制主要有2个主要指令,limit_req_zone和limit_req。如下代码:

limit_req_zone指令定义了速度限制的参数,同时在出现的上下文中启用速率限制。(在本例中是针对于 /login/ URI的所有请求)

limit_requ_zone 指令通常定义在HTTP块中,这样可以用于多个上下文。它包含3个参数:

Key - 定义应用限制的请求特征。 在这个例子中,它是Nginx变量$binary_remote_addr ,它保存着客户端IP地址的二进制表示。 这意味着我们将每个唯一的IP地址限制为由第三个参数定义的请求速率(我们使用这个变量,因为它比客户端IP地址的字符串表示$remote_addr占用更少的空间)。

Zone - 定义用于存储每个IP地址状态的共享内存区域以及访问请求受限URL的频率。 将信息保存在共享内存中意味着它可以在Nginx工作进程之间共享。 

定义有两个部分: zone= keyword标识的区域名称和冒号后面的大小。 大约16,000个IP地址的状态信息需要1兆字节,所以我们的区域可以存储大约160,000个地址。 如果Nginx需要添加一个新条目时,存储空间将被耗尽,它将删除最旧的条目。 

如果释放的空间不足以容纳新记录,则Nginx返回状态码503(Temporarily Unavailable) 。 此外,为了防止内存耗尽,每当Nginx创建一个新条目时,最多可以删除两个在前60秒内没有使用的条目。

Rate - 设置最大请求率。 在这个例子中,速率不能超过每秒10个请求。 Nginx实际上以毫秒粒度跟踪请求,所以这个限制对应于每100毫秒1个请求。 由于我们不允许爆发,这意味着如果请求在前一个允许的时间之后小于100毫秒时被拒绝。

limit_req_zone指令为速率限制和共享内存区域设置参数,但实际上并不限制请求速率。 

因此,您需要通过在其中包含limit_req指令来将限制应用于特定location或server块。 在这个例子中,我们是对/login/的URI速率限制请求。

因此,现在每个唯一的IP地址被限制,/login/每秒10个请求 - 或者更确切地说,在前一个100毫秒内不能请求该URL。

处理并发

如果我们在100毫秒内得到两个请求会怎么样? 对于第二个请求,Nginx将状态码503返回给客户端。 这可能不是我们想要的,因为应用程序本质上是突发性的。

相反,我们想要缓冲任何多余的请求并及时提供服务。 这是我们使用burst参数limit_req ,在这个更新的配置:

burst参数定义了客户端可以超过区域指定的速率(使用我们的示例mylimit区域,速率限制为每秒10个请求,或每100毫秒1个)可以产生多少个请求。 

在前一个请求到达100毫秒后的请求被放入一个队列中,这里我们将队列大小设置为20。

这意味着如果21个请求同时从一个给定的IP地址到达,Nginx立即将第一个请求转发到上游服务器组,并将剩下的20个放入队列中。 然后,它每100毫秒转发一个排队的请求,并且只有当传入的请求使排队请求的数量超过20时才返回503给客户端。

无延迟队列

具有burst的配置会导致流量畅通,但不是很实用,因为它可能会使您的网站显得很慢。 

在我们的例子中,队列中的第20个数据包等待2秒钟被转发,此时对其的响应可能对客户端不再有用。 要解决这种情况,请将nodelay参数与burst参数一起添加:

通过nodelay参数,Nginx仍然根据burst参数在队列中分配时隙,并且强加配置的速率限制,但是不排除转发排队的请求。 相反,当请求到达“太快”时,Nginx会立即转发,只要队列中有一个可用的时隙。 它将该插槽标记为“已占用”,并且不会将其释放以供其他请求使用,直到经过适当的时间(在本例中为100毫秒之后)。

假设像以前一样,20个时隙的队列是空的,21个请求同时从给定的IP地址到达。 Nginx立即转发所有21个请求,并将队列中的20个插槽标记为已占用,然后每100毫秒释放1个插槽(如果有25个请求,Nginx会立即转发21个插槽,标记20个插槽,拒绝4个请求状态503 )。

现在假设在第一组请求之后101毫秒被转发,另外20个请求同时到达。 队列中只有1个插槽被释放,所以Nginx转发1个请求,并拒绝其他19个状态为503的队列。 如果在20个新请求到达之前经过了501毫秒,那么5个空闲空间,所以Nginx立即转发5个请求,拒绝15个请求。

效果相当于每秒10个请求的速率限制。 如果您希望在不限制请求之间的允许间隔的情况下施加速率限制,则nodelay选项非常有用。

注意:对于大多数部署,我们建议将burst和nodelay参数包含到limit_req指令中。

高级配置示例

通过将基本速率限制与其他Nginx功能相结合,您可以实现更多细微的流量限制。

白名单

此示例显示如何对不在“白名单”上的任何人的请求施加速率限制。

这个例子使用了geo和map指令。 geo块为白名单中的IP地址分配一个0值到$limit值,其他0 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地址应用速率限制:

白名单上的IP地址与第一个速率限制( req_zone )不匹配,但匹配第二个( req_zone_wl ),因此每秒限制为15个请求。 

不在白名单上的IP地址与两个速率限制相匹配,所以限制性较强的一个适用:每秒5个请求。

配置相关功能

记录

默认情况下,Nginx 记录由于速率限制而延迟或丢弃的请求,如下例所示:

 2017/06/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2, server: Nginx.com, request: "GET / HTTP/1.0", host: "Nginx.com"

日志条目中的字段包括:

limitingrequests - 指示日志条目记录速率限制。

excess - 此请求表示的配置速率每毫秒的请求数。

zone - 定义强加的限制的区域。

client - 发出请求的客户client IP地址。

server - server IP地址或主机名。

request - 客户端request实际HTTP请求。

host - Host HTTP头的值。

默认情况下,Nginx在error级别记录被拒绝的请求,如上例中的[error]所示(它记录延迟的请求在一个较低的级别,所以默认info )。 要更改日志级别,请使用limit_req_log_level指令。 在这里,我们设置了拒绝的请求来记录warn级别:

错误代码发送到客户端

默认情况下,当客户端超出速率限制时,Nginx以状态码503作为响应。 

使用limit_req_status指令来设置一个不同的状态码(在这个例子中是444 ):

拒绝所有请求到特定的位置

如果您想要拒绝所有特定URL的请求,而不是限制它们,请为其配置一个块并包含all指令:

小结

我们已经介绍了Nginx和Nginx Plus提供的更多速率限制的功能,包括为HTTP请求的不同位置设置请求速率,以及配置其他功能来限制速度,如burst和nodelay参数。我们还介绍了对客户端IP地址配置不同的白名单和黑名单来匹配不同的高级配置,并介绍了如何记录被拒绝和延迟的记录。

加Java架构师群获取Java工程化、高性能及分布式、高性能、深入浅出。高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的直播免费学习权限 都是大牛带飞 让你少走很多的弯路的 群..号是:855801563 对了 小白勿进 最好是有开发经验的哦~

11-21 17:33