参考文档:
http://nginx.org/en/docs/http/ngx_http_limit_req_module.html
http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html
ngx_http_limit_conn_module
连接频率限制
ngx_http_limit_req_module
请求频率限制
http协议的连接与请求
HTTP是建立在TCP基础上,在完成HTTP请求需要先建立TCP三次握手(称为TCP连接),在连接的基础上在HTTP请求。
HTTP请求建立在一次TCP连接基础上
一次TCP请求至少产生一次HTTP请求(一个连接可以有多个请求,建立一次三次握手可以发送多个请求)
HTTP协议 | 连接关系 |
---|---|
HTTP1.0 | TCP不能复用 |
HTTP1.1 | 顺序性TCP复用 |
HTTP2.0 | 多路复用TCP复用 |
参考文档:http://nginx.org/en/docs/http/ngx_http_limit_req_module.html
# 必须在全局定义请求限制
Syntax: limit_conn_zone key zone=name:size rate=rate; #zone的名称大小及速率
Default: -
Context: http
# 引用请求限制
Syntax: limit_conn zone number [burst=number] [nodelay];
Default: -
Context: http,server,location
# http模块配置
limit_req_zone $binary_remote_addr zone=req_zone:1m rate=1r/s;
> $binary_remote_addr比$remote_addr更节省字节,是限制同一客户端ip地址
> zone=req_zone:1m表示名称是req_zone的内存区域,用来存储访问的频次信息,1m用来存储session,1m能存储16000个状态
> rate限制速率为1秒最多1个IP请求,rete的值必须为整数
# location模块引用配置
limit_req zone=req_zone;
# location模块引用req_zone配置,请求超过1r/s,剩下的将被延迟处理,burst=3缓冲区大小3,nodelay瞬时提供处理(burst + rate)个请求的能力,多余的直接返回503
limit_req zone=req_zone burst=3 nodelay;
http {
...
limit_req_zone $binary_remote_addr zone=req_zone:1m rate=1r/s;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html/www;
index index.html;
location / {
root /usr/share/nginx/html/www;
index index.html;
limit_req zone=req_zone; # 限制请求
#limit_req zone=req_zone burst=3 nodelay;
}
}
}
# burst :
# 爆发的意思,这个配置的意思是设置一个大小为5的缓冲区,当有大量请求(爆
# 发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内等待,
# 但是这个等待区里的位置只有3个,超过的请求会直接报503的错误然后返回。
# nodelay :
# 瞬时提供处理(burst + rate)个请求的能力,
# 请求超过(burst + rate)的时候就会直接返回503,永远不存在请求需要等待的情况
# 如果没有设置nodelay,则所有请求会依次等待排队
#安装压测工具
[root@web-node2 ~]$ yum install -y httpd-tools
location / {
root /usr/share/nginx/html/www;
index index.html;
limit_req zone=req_zone;
}
# 使用ab测试工具,发起10个并发请求
[root@web-node2 ~]# ab -n 10 -c 10 http://192.168.1.17/index.html
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.1.17 (be patient).....done
Server Software: nginx/1.14.1
Server Hostname: 192.168.1.17
Server Port: 80
Document Path: /index.html
Document Length: 6 bytes
Concurrency Level: 10 # 10并发个请求
Time taken for tests: 0.011 seconds
Complete requests: 10 # 总共请求10次
Failed requests: 9 #失败9次
(Connect: 0, Receive: 0, Length: 9, Exceptions: 0)
Write errors: 0
Non-2xx responses: 9
Total transferred: 3700 bytes
HTML transferred: 1923 bytes
Requests per second: 907.94 [#/sec] (mean)
Time per request: 11.014 [ms] (mean) # 11ms就压测结束
Time per request: 1.101 [ms] (mean, across all concurrent requests)
Transfer rate: 328.06 [Kbytes/sec] received
>>>可以看到一共10个请求,9个请求都失败了。且11ms就完成了压测
# 查看 /var/log/nginx/access.log,印证了只有一个请求成功了,其它就是都直接返回了503
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:34:04 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
location / {
root /usr/share/nginx/html/www;
index index.html;
#limit_req zone=req_zone; # 限制请求
limit_req zone=req_zone burst=3;
}
# 查看当前的tcp连接数
[root@mysql-slave ~]$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 6
[root@mysql-slave ~]$
# 建立10个连接并发10
[root@mysql-slave ~]$ ab -n 10 -c 5 http://192.168.1.17/index.html
....
Server Software: nginx/1.14.1
Server Hostname: 192.168.1.17
Server Port: 80
Document Path: /index.html
Document Length: 6 bytes
Concurrency Level: 10 # 10并发个请求
Time taken for tests: 0.012 seconds
Complete requests: 10 # 总共请求10次
Failed requests: 6 # 失败了6次
Total transferred: 3250 bytes
HTML transferred: 1302 bytes
Requests per second: 3.32 [#/sec] (mean)
Time per request: 1505.007 [ms] (mean)
Time per request: 301.002 [ms] (mean, across all concurrent requests)
Transfer rate: 1.05 [Kbytes/sec] received
...
>>>> 可以看到一共10个请求,6个请求都失败了
# ab测试第一秒时ESTABLISHED由4变为6,即建立了2个TCP连接,
# 同时TIME_WAIT=9 表示有服务器端主动断开了9个TCP连接,即9个请求被瞬时拒绝
[root@mysql-slave ~]$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 6
TIME_WAIT 9
[root@mysql-slave ~]$
# ab测试完,
# TIME_WAIT=10 表示有服务器端主动断开了10个TCP连接,增加的1个TIME_WAIT是因为有1个在缓存队列的请求被处理完毕了,所以断开了连接
[root@mysql-slave ~]$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 4
TIME_WAIT 10
[root@mysql-slave ~]$
# 查看 /var/log/nginx/access.log日志
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:54 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:55 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:18:13:56 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
>>>在18:13:53的时候,可以看到压测第1秒时,成功处理了1个请求,另外有6个请求瞬间返回了503,剩下的4个请求每隔1s处理一次
>>>这是因为设置了burst=3,在服务器接收到10个并发请求后,先处理1个请求,同时将3个请求放入burst缓冲队列中,等待处理,
而超过(burst+1)数量的请求就被直接抛弃了,即直接抛弃了6个请求。缓冲区的剩余3个请求,1秒执行1个。
location / {
root /usr/share/nginx/html/www;
index index.html;
#limit_req zone=req_zone; # 限制请求
limit_req zone=req_zone burst=3 nodelay; #缓冲区大小为3,nodelay瞬时提供处理(burst + rate)个请求
}
[root@web-node2 ~]# ab -n 60 -c 20 http://192.168.1.17/index.html
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.1.17 (be patient).....done
Server Software: nginx/1.14.1
Server Hostname: 192.168.1.17
Server Port: 80
Document Path: /index.html
Document Length: 6 bytes
Concurrency Level: 20
Time taken for tests: 0.048 seconds
Complete requests: 10
Failed requests: 6 # 成功了4个请求(3+1)
(Connect: 0, Receive: 0, Length: 56, Exceptions: 0)
Write errors: 0
Non-2xx responses: 56
Total transferred: 3250 bytes
HTML transferred: 1302 bytes
Requests per second: 359.27 [#/sec] (mean) # 吞吐量
....
>>> 可以看到一共10个请求,失败了6个,成功了4个,缓冲区生效了
# 查看 /var/log/nginx/access.log日志,可以发现在1s内,服务器端处理了4个请求(峰值速度:burst+原来的处理速度)。对于剩下的6个请求,直接返回503
[root@mysql-slave ~]$ tail -f /var/log/nginx/access.log
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 200 6 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
192.168.1.13 - - [14/Nov/2018:17:49:53 +0800] "GET /index.html HTTP/1.0" 503 213 "-" "ApacheBench/2.3" "-"
# 查看/var/log/nginx/error.log日志,发现有6个请求被直接拒绝了,没有延时请求。
[root@mysql-slave ~]$ tail -f /var/log/nginx/error.log
2018/11/14 17:49:53 [error] 20735#20735: *478 limiting requests, excess: 3.981 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *479 limiting requests, excess: 3.980 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *480 limiting requests, excess: 3.980 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *481 limiting requests, excess: 3.978 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *482 limiting requests, excess: 3.978 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
2018/11/14 17:49:53 [error] 20735#20735: *483 limiting requests, excess: 3.977 by zone "req_zone", client: 192.168.1.13, server: localhost, request: "GET /index.html HTTP/1.0", host: "192.168.1.17"
参考文档:http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html
# 全局定义连接限制
句法: limit_conn_zone key zone-name:size;
默认: -
语境: http
# 引用连接限制
句法: limit_conn zone number;
默认: -
语境: http,server,location
# 实例配置[root@mysql-slave /etc/nginx]$ vim nginx.conf
...
limit_conn_zone $binary_remote_addr zone=conn_zone:10m;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html/www;
index index.html;
location / {
root /usr/share/nginx/html/www;
index index.html;
#limit_req zone=req_zone;
#limit_req zone=req_zone burst=3 nodelay;
#同一时间只允许一个客户端IP连接
limit_conn conn_zone 1;
}
}