在系统高峰期间存在高并发、系统被外界攻击等一系列潜在风险,为此Nginx提供了限流的策略,以规避掉潜在风险,整体提高系统的安全性。Nginx使用漏桶算法按请求速率对模块进行限速,即能够强行保证请求的实时处理速度不会超过设置的阈值。漏桶算法思想如下:
1)水(请求)从上方倒入水桶,从水桶下方流出(被处理);
2)来不及流出的水存在水桶中(缓冲),以固定速率流出;
3)水桶满后水溢出(丢弃);
4)请求放入缓存,匀速处理,多余的请求直接丢弃。
Nginx限制频率的代码如代码清单4-16所示。
limit_conn_log_level notice; limit_conn_status 503; limit_conn_zone $server_name zone=perserver:10m; limit_conn_zone $binary_remote_addr zone=perip:10m; limit_req_zone $binary_remote_addr zone=zachary:100m rate=2r/s; server { listen 80; limit_conn perserver 250; limit_conn perip 2; limit_req zone=zachary; limit_rate_after 5m; limit_rate 800k; location / { proxy_pass http:// zachary.sh.cn; } }
其中,$binary_remote_addr通过remote_addr这个标识来限制同一客户端IP地址。
zone=perip:10m;表示生成一个大小为10MB,名称为one的内存区域,用来存储访问的频次信息。
rate=2r/s表示允许相同标识的客户端的访问频次为每秒2次,zone=zachary表示使用zachary区域来做限制。
当单个IP在50ms内发送了8个请求时,处理流程为:Nginx的限流统计是基于毫秒的,设置的速度是2r/s,换算成毫秒是500ms内单个IP允许通过1个请求。所以,当前只有1个请求会被处理,其余7个请求会被直接拒绝。真实网络环境中请求到来不是匀速的,很可能有“突发请求”的情况,对于突发请求的处理方式是将其放入缓存,而不是直接拒绝。
Nginx限制频率并缓存处理(排队等待)的代码如代码清单4-17所示。
limit_req_zone $binary_remote_addr zone=zachary:100m rate=2r/s; server { listen 80; limit_conn perserver 250; limit_conn perip 2; limit_req zone=zachary burst=3; limit_rate_after 5m; limit_rate 800k; location / { proxy_pass http:// zachary.sh.cn; } }
其中,burst=3表示设置了一个大小为3的缓冲区,即每个IP最多允许3个突发请求的到来,burst的作用是让多余的请求可以先放到队列里,慢慢处理。
如代码清单4-17所示,1个请求被立即处理,3个请求被放到burst队列里,另外4个请求被拒绝。被放到burst队列里的3个请求,系统会每隔500ms(rate=2r/s)取一个请求进行处理,最后一个请求要等待1.5s才会被处理,显然请求排队的时间会比较长。
Nginx限制频率并缓存处理(不排队等待)的代码如代码清单4-18所示。
limit_req_zone $binary_remote_addr zone=zachary:100m rate=2r/s; server { listen 80; limit_conn perserver 250; limit_conn perip 2; limit_req zone=zachary burst=3 nodelay; limit_rate_after 5m; limit_rate 800k; location / { proxy_pass http:// zachary.sh.cn; } }
其中,nodelay表示超过访问频次而且缓冲区也满了的时候就会直接返回503,如果没有设置,则所有请求会排队等待。
nodelay能降低排队时间,nodelay参数允许请求在排队的时候被立即处理,只要请求能够进入burst队列,就会立即被后台worker处理。
当单个IP在50ms内发送了8个请求时,处理流程为:1个请求被立即处理,3个请求被放到burst队列里,另外4个请求被拒绝。
由于队列中的请求同时具有了被处理的资格,所以4个请求是同时被处理的,花费的时间自然变短了。
使用Nginx限流时,应当设置自定义状态,如代码清单4-19所示。
limit_req_zone $binary_remote_addr zone=zachary:100m rate=2r/s; server { listen 80; limit_conn perserver 250; limit_conn perip 2; limit_req zone=zachary burst=3 nodelay; limit_req_status 600; limit_rate_after 5m; limit_rate 800k; location / { proxy_pass http:// zachary.sh.cn; } }