Nginx提供了一个叫ngx_http_limit_req_module的模块进行流量控制,该模块使用漏桶算法实现
官方文档参考:Module ngx_http_limit_req_module
配置共享内存空间用来保存key的状态,尤其是当前过量请求个数。key的值可以是文本、变量或者是一个由前面两种类型值组成的比较结果。请求中携带key对应的值如果为空,将不会进行计数
语法:limit_req_zone key zone=name:size rate=rate;
默认值:无
上下文:http
示例:
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
limit_req_zone $binary_remote_addr zone=ten:10m rate=10r/m;
server {
......
说明:
配置了一个名字为ten的共享内存空间(zone=ten)用来保存不同客户端IP地址($binary_remote_addr,比起$remote_addr的优势是占用内存空间固定,IPv4固定占用4字节,IPv6固定占用16字节。在32位系统,每一个IP在32位系统将占用64字节、在64位系统将占用128字节来保存状态。1m空间在32位系统能保存1w6多个IP的状态,在64位系统能保存8k多个IP的状态)的过量请求个数,这个空间占用10m内存(10m)并且平均请求处理速率不能超过每分钟10个请求(rate=10r/m)
提前预估key的数量分配合理的内存空间,避免指定的内存空间被耗尽
速率使用请求数量/秒(r/s)来指定,如果速率低于每秒一个请求,可以使用请求数量/分钟(r/m)来指定
语法:limit_req zone=name [burst=number] [nodelay];
默认值:无
上下文:http,server,location
示例:
location /Service {
proxy_pass http://127.0.0.1:8080/Service/1.jsp;
limit_req zone=ten burst=1 nodelay;
limit_req_status 503;
}
说明:
针对特定URL,设置使用的共享内存空间(zone=ten)以及缓存队列长度(burst=1)
如果请求速率超过了共享内存空间设置的请求执行速率,过量的请求将被延迟处理,如果过量的请求个数超过了缓存队列长度,超出部分请求将直接返回error
如果不希望延迟处理过量的请求,可以设置nodelay参数,此时过量的请求将直接返回error
语法:limit_req_log_level info | notice | warn | error;
默认值:limit_req_log_level error;
上下文:http,server,location
说明:
用来设置请求过量时,延迟处理的请求和直接拒绝的请求在error log中记录的日志级别
需要注意的是延迟处理的请求日志级别将比直接拒绝的请求日志级别低一级,如果使用默认值limit_req_log_level error,则直接拒绝的请求将记录error日志,延迟处理的请求将记录warn日志
2017/11/01 20:34:44 [warn] 22401#0: *1 limiting requests, excess: 5.352 by zone "ten",
client: 192.168.239.1, server: localhost, request: "GET /Service HTTP/1.1", host: "192.168.239.129"
测试时发现,error.log中并不会记录延迟处理的请求信息
语法:limit_req_status code;
默认值:limit_req_status 503;
上下文:http,server,location
说明:
当请求由于过量被拒绝时返回的HTTP状态码,access.log中的日志记录如下:
192.168.239.1 - - [01/Nov/2017:20:34:44 -0700] "GET /Service HTTP/1.1" 502
575 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/61.0.3163.100 Safari/537.36"
完整的Nginx配置文件如下:
worker_processes 1;
error_log logs/error.log notice;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
limit_req_zone $binary_remote_addr zone=ten:10m rate=1r/s;
server {
listen 80;
server_name localhost;
location /Service {
proxy_pass http://127.0.0.1:8080/Service/1.jsp;
limit_req zone=ten burst=5 nodelay;
limit_req_log_level warn;
limit_req_status 502;
}
}
}
安装ab,并进行测试:
apt-get install apache2-utils
从测试结果可以看到,1s内发送7条请求,总共6条成功(1条正常处理,5条延迟处理),1条失败,查询access.log,可以看到前6条请求返回200,最后一条请求返回502
root@ubuntu:/usr/bin# ab -c 1 -n 7 http://192.168.239.129/Service
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.239.129 (be patient).....done
Server Software: nginx/1.12.2
Server Hostname: 192.168.239.129
Server Port: 80
Document Path: /Service
Document Length: 59 bytes
Concurrency Level: 1
Time taken for tests: 0.017 seconds
Complete requests: 7
Failed requests: 1
(Connect: 0, Receive: 0, Length: 1, Exceptions: 0)
Non-2xx responses: 1
Total transferred: 2137 bytes
HTML transferred: 527 bytes
Requests per second: 418.19 [#/sec] (mean)
Time per request: 2.391 [ms] (mean)
Time per request: 2.391 [ms] (mean, across all concurrent requests)
Transfer rate: 124.67 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 0 2 4.0 1 11
Waiting: 0 1 3.7 0 10
Total: 0 2 4.0 1 11
Percentage of the requests served within a certain time (ms)
50% 1
66% 1
75% 1
80% 1
90% 11
95% 11
98% 11
99% 11
100% 11 (longest request)
修改Nginx配置,去掉burst=5,再次使用ab进行测试,发现只有1个请求成功,6个请求失败,查看access.log,可以看到只有第一条请求返回200,剩下6条请求都返回502