开源Linux
专注分享开源技能知识
流量限定(rate-limiting),是Nginx中一个非常实用却常常被缺点理解和缺点配置的功能。我们可以用来限定用户在给定时间内HTTP要求的数量。要求,可以是一个大略网站首页的GET要求,也可以是登录表单的POST要求。

流量限定可以用作安全目的,比如可以减慢暴力密码破解的速率。通过将传入要求的速率限定为真实用户的范例值,并标识目标URL地址(通过日志),还可以用来抵御DDOS攻击。更常见的情形,该功能被用来保护上游运用做事器不被同时太多用户要求所压垮。
本篇文章将会先容Nginx的 流量限定 的根本知识和高等配置,”流量限定”在Nginx Plus中也适用。
Nginx如何限流Nginx的”流量限定”利用漏桶算法(leaky bucket algorithm),该算法在通讯和分组交流打算机网络中广泛利用,用以处理带宽有限时的突发情形。就好比,一个桶口在倒水,桶底在漏水的水桶。如果桶口倒水的速率大于桶底的漏水速率,桶里面的水将会溢出;同样,在要求处理方面,水代表来自客户真个要求,水桶代表根据”前辈先出调度算法”(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指令在涌现的高下文中启用流量限定(示例中,对付”/login/”的所有要求)。limit_req_zone指令常日在HTTP块中定义,使其可在多个高下文中利用,它须要以下三个参数:
Key - 定义运用限定的要求特性。示例中的Nginx变量remote_addr,占用更少的空间)Zone - 定义用于存储每个IP地址状态以及被限定要求URL访问频率的共享内存区域。保存在内存共享区域的信息,意味着可以在Nginx的worker进程之间共享。定义分为两个部分:通过zone=keyword标识区域的名字,以及冒号后面跟区域大小。16000个IP地址的状态信息,大约须要1MB,以是示例中区域可以存储160000个IP地址。Rate - 定义最大要求速率。在示例中,速率不能超过每秒10个要求。Nginx实际上以毫秒的粒度来跟踪要求,以是速率限定相称于每100毫秒1个要求。由于不许可”突发情形”(见下一章节),这意味着在前一个要求100毫秒内到达的要求将被谢绝。当Nginx须要添加新条款时存储空间不敷,将会删除旧条款。如果开释的空间仍不足容纳新记录,Nginx将会返回 503状态码(Service Temporarily Unavailable)。其余,为了防止内存被耗尽,Nginx每次创建新条款时,最多删除两条60秒内未利用的条款。
limit_req_zone指令设置流量限定和共享内存区域的参数,但实际上并不限定要求速率。以是须要通过添加limit_req指令,将流量限定运用在特定的location或者server块。在上面示例中,我们对/login/要求进行流量限定。
现在每个IP地址被限定为每秒只能要求10次/login/,更准确地说,在前一个要求的100毫秒内不能要求该URL。
处理突发如果我们在100毫秒内吸收到2个要求,怎么办?对付第二个要求,Nginx将给客户端返回状态码503。这可能并不是我们想要的结果,由于运用实质上趋向于突发性。相反地,我们希望缓冲任何逾额的要求,然后及时地处理它们。我们更新下配置,在limit_req中利用burst参数:
location /login/ { limit_req zone=mylimit burst=20; proxy_pass http://my_upstream;}
burst参数定义了超出zone指定速率的情形下(示例中的mylimit区域,速率限定在每秒10个要求,或每100毫秒一个要求),客户端还能发起多少要求。上一个要求100毫秒内到达的要求将会被放入行列步队,我们将行列步队大小设置为20。
这意味着,如果从一个给定IP地址发送21个要求,Nginx会立即将第一个要求发送到上游做事器群,然后将余下20个要求放在行列步队中。然后每100毫秒转发一个排队的要求,只有当传入要求使行列步队中排队的要求数超过20时,Nginx才会向客户端返回503。
无延迟的排队配置burst参数将会使通讯更流畅,但是可能会不太实用,由于该配置会使站点看起来很慢。在上面的示例中,行列步队中的第20个包须要等待2秒才能被转发,此时返回给客户真个相应可能不再有用。要办理这个情形,可以在burst参数后添加nodelay参数:
location /login/ { limit_req zone=mylimit burst=20 nodelay; proxy_pass http://my_upstream;}
利用nodelay参数,Nginx仍将根据burst参数分配行列步队中的位置,并运用已配置的速率限定,而不是清理行列步队中等待转发的要求。相反地,当一个要求到达“太早”时,只要在行列步队中能分配位置,Nginx将立即转发这个要求。将行列步队中的该位置标记为”taken”(霸占),并且不会被开释以供另一个要求利用,直到一段韶光后才会被开释(在这个示例中是,100毫秒后)。
假设如前所述,行列步队中有20个空位,从给定的IP地址发出的21个要求同时到达。Nginx会立即转发这个21个要求,并且标记行列步队中霸占的20个位置,然后每100毫秒开释一个位置。如果是25个要求同时到达,Nginx将会立即转发个中的21个要求,标记行列步队中霸占的20个位置,并且返回503状态码来谢绝剩下的4个要求。
现在假设,第一组要求被转发后101毫秒,另20个要求同时到达。行列步队中只会有一个位置被开释,以是Nginx转发一个要求并返回503状态码来谢绝其他19个要求。如果在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/64 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地址对应的$limit变量分配一个值0,给其它不在白名单中的分配一个值1。然后我们利用一个映射将这些值转为key,如下:
如果变量的值是,limit_key变量将被赋值为空字符串如果变量的值是,limit_key变量将被赋值为客户端二进制形式的IP地址 两个指令合营利用,白名单内IP地址的$limit_key变量被赋值为空字符串,不在白名单内的被赋值为客户真个IP地址。当limit_req_zone后的第一个参数是空字符串时,不会运用“流量限定”,以是白名单内的IP地址(10.0.0.0/8和192.168.0.0/24 网段内)不会被限定。其它所有IP地址都会被限定到每秒5个要求。limit_req指令将限定运用到/的location块,许可在配置的限定上最多超过10个数据包的突发,并且不会延迟转发。
location包含多limit_req指令我们可以在一个location块中配置多个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_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, server: nginx.com, <br>request: "GET / HTTP/1.0", host: "nginx.com"
日志条款中包含的字段:
limiting requests - 表明日志条款记录的是被“流量限定”要求excess - 每毫秒超过对应“流量限定”配置的要求数量zone - 定义履行“流量限定”的区域client - 发起要求的客户端IP地址server - 做事器IP地址或主机名request - 客户端发起的实际HTTP要求host - HTTP报头中host的值默认情形下,Nginx以error级别来记录被谢绝的要求,如上面示例中的[error]所示(Ngin以较低级别记录延时要求,一样平常是info级别)。如要变动Nginx的日志记录级别,须要利用limit_req_log_level指令。这里,我们将被谢绝要求的日志记录级别设置为warn:
location /login/ { limit_req zone=mylimit burst=20 nodelay; limit_req_log_level warn; proxy_pass http://my_upstream;}
发送到客户真个缺点代码
一样平常情形下,客户端超过配置的流量限定时,Nginx相应状态码为503(Service Temporarily Unavailable)。可以利用limit_req_status指令来设置为其它状态码(例如下面的444状态码):
location /login/ { limit_req zone=mylimit burst=20 nodelay; limit_req_status 444;}
指定location谢绝所有要求
如果你想谢绝某个指定URL地址的所有要求,而不是仅仅对其限速,只须要在location块中配置deny all指令:
location /foo.php { deny all;}
总结
前文已经涵盖了Nginx和Nginx Plus供应的“流量限定”的很多功能,包括为HTTP要求的不同loation设置要求速率,给“流量限定”配置burst和nodelay参数。还涵盖了针对客户端IP地址的白名单和黑名单运用不同“流量限定”的高等配置,阐述了如何去日志记录被谢绝和延时的要求。
原文:
https://www.cnblogs.com/zjfjava/p/10947264.html