一、并发访问限制
ngx_http_limit_conn_module
是一个默认安装的内置模块,被用来限制在某一个关键字维度上的最大并发数量,通常情况下,这个维度被设置为访问者的 IP 。在计算的一个连接当前的并发数量时,不是一连接就会被计数,而是当所有请求头都被读完才计数。它的示例配置为:
1 2 3 4 5 6 7 8 9 10 |
http { limit_conn_zone $binary_remote_addr zone=addr:10m; # ... server { # ... location /download/ { limit_conn addr 1; } } } |
以上配置通过 limit_conn_zone
指令定义了一个名为 addr
的并发限制器,它以 $binary_remote_addr
(即访问的 IP 地址) 作为 key,分配一块 10m 的空间来保存所有的连接数量。
而后的对应的 server 段中,使用这个 addr,限制同一时刻最多只能有 1 个连接。所以整个配置的意思就是:限制单个 IP 同一时刻最多有 3 个访问连接。
相关的参考文档:Module ngx_http_limit_conn_module 。
1.1 案例一:针对 IP 的并发数量限制
当前有一个站点 d2.dyxmq.cn
,希望同一时刻同一 IP 最多只能 3 个并发访问:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
http { limit_conn_zone $binary_remote_addr zone=addr:10m server { listen 80; server_name d2.dyxmq.cn; limit_conn addr 3; limit_rate 1m; # 限制访问速率 1M/s location / { root html; index index.html index.htm; } access_log logs/access.log main; error_log logs/error.log; } } |
网站的根目录下有个文件 master.zip,大小 8.8M,在另一台客户端上使用 ab
命令执行并发测试:
1 |
ab -c 5 -n 10 https://d2.dyxmq.cn/master.zip # 同一时刻 5 个并发连接,一共 10 个连接 |
测试过程中 Nginx 的访问日志:
1 2 3 4 5 6 7 8 9 10 11 |
> cat access.log 192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:17:45:27 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:17:45:27 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:17:45:27 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" |
可以看到,除了 3 个连接的的返回值为 200 以外,其他的都返回状态码 503 。
为什么三个 200 访问的日志在最后呢?
因为下载的文件内容是 8.8M,配置中有限制最大下载速率为 1M/s,所以它大概需要 9 秒才能下载完成,状态码是在连接返回完成才打印出来的,并且可以看到 503 状态码和 200 状态码的日志间隔差不多刚好 8-9s 。
那为什么要限制下载速率呢?
当前在内网环境下,下载速度非常快,8.8M 的文件几乎 1S 内就能下载完成,连接很难并发,即使并发了,10 个连接也很有可能有多个成功了。
1.2 针对服务端同一时刻的访问频率限制
和上面同样的环境,我们不限制单个 IP 的并发访问数量,而希望同一时刻服务器最多处理 10 个连接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
http { limit_conn_zone $server_name zone=web_server:10m; server { listen 80; server_name d2.dyxmq.cn; limit_conn web_server 10; limit_conn_log_level info; limit_rate 1m; location / { root html; index index.html index.htm; } access_log logs/access.log main; error_log logs/error.log; } } |
执行命令测试:
1 |
ab -c 11 -n 15 https://d2.dyxmq.cn/master.zip # 同时产生 11 个连接,一共访问 15 次 |
nginx 访问日志:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "/master.zip" 503 537 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" 192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-" |
限制了同一时刻服务端只能有 10 个连接之后,现象也和上面的一样,15 个连接有 5 个返回错误,但是不同的是这些连接可以同时来自于同一个 IP 。
1.3 多关键字配合使用
上面的基于 IP 和服务端访问限制可以同时使用,并且不只是这两个字段可以配合使用,其他的变量也都可以同时使用,具体的变量可以参考:Alphabetical index of variables 。
例如我们可以同时限制单个 IP 并发连接数为 3,并且同时访问服务端的连接数为 100:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
http { limit_conn_zone $server_name zone=web_server:10m; limit_conn_zone $binary_remote_addr zone=addr:10m server { listen 80; server_name d2.dyxmq.cn; limit_conn web_server 100; # 同一时刻最多 100 个连接访问服务端 limit_conn addr 3; # 同一时刻同一 IP 最多 3 个连接访问 limit_conn_log_level info; limit_rate 1m; location / { root html; index index.html index.htm; } access_log logs/access.log main; error_log logs/error.log; } } |
二、限制访问频率
ngx_http_limit_req_module
模块用于限制每个 IP 访问某个关键字维度的请求速率,其参数用法如下:
1 |
limit_req_zone key zone=name:size rate=rate; |
以上的配置创建一个速率限制器,限制单个 IP 在 key 这个维度上的访问速率。和上面一样,这个 key 也通常被设置成访问者的 IP 。使用时在对应的 server 段内设置:
1 |
limit_req zone=name [burst=number] [nodelay]; |
burst 表示令牌数量,连接满了之后,给接下来的连接发放令牌进行等待。令牌数量超出后,可以选择继续等待令牌或者直接返回错误状态。
这里的逻辑可以看成去银行办业务:人多的时候需要等号,number 可以看成最大的等号数量,rate 可以看成银行的窗口个数。银行同一时刻处理 rate 个客户的请求,并且同时允许 number 个客户排队,超出后,根据 nodelay 是否被设置来判断该连接是应该被丢弃还是等待。
参考文档:Module ngx_http_limit_req_module 。
2.1 限制每秒只处理同一个用户的一个请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
http { # 以请求 IP 作为 KEY,设置访问频率为 1 秒 1 次请求 limit_req_zone $binary_remote_addr zone=addr:10m rate=1r/s; server { listen 80; server_name d2.dyxmq.cn; # 设置队列为 5,最多有 5 个连接等待,超出的不继续等待 limit_req zone=addr burst=5 nodelay; limit_rate 1m; location / { root html; index index.html index.htm; } access_log logs/access.log main; error_log logs/error.log; } } |
为了避免大文件下载耗时,这里不再和上面一样下载大文件,使用小文件测试:
1 |
ab -c 6 -n 100 https://d2.dyxmq.cn # 6 个并发连接,访问 100 次 |
得到日志后,复制到当前目录下,分别分析 200 和 503 响应的次数:
1 2 3 4 |
> grep "503" access.log | wc -l 94 > grep "200" access.log | wc -l 6 |
nginx 第一秒处理第一个请求,同时给接下来的 5 个请求排队,剩下的都直接返回 503,所以返回 200 的次数为 6,503 的次数为 94 。 ab
返回的结果也能看到成功和失败的数量:
评论