nginx访问限频

一、并发访问限制

ngx_http_limit_conn_module是一个默认安装的内置模块,被用来限制在某一个关键字维度上的最大并发数量,通常情况下,这个维度被设置为访问者的IP。在计算的一个连接当前的并发数量时,不是一连接就会被计数,而是当所有请求头都被读完才计数。它的示例配置为:

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.maqian.co,希望同一时刻同一IP最多只能3个并发访问:

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m
    server {
        listen       80;
        server_name  d2.maqian.co;
        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命令执行并发测试:

ab -c 5 -n 10 http://d2.maqian.co/master.zip # 同一时刻5个并发连接,一共10个连接

测试过程中Nginx的访问日志:

> cat access.log
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:27 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:27 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:27 +0800] GET "d2.maqian.co/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个连接:

http {
    limit_conn_zone $server_name zone=web_server:10m;
    server {
        listen       80;
        server_name  d2.maqian.co;
        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;
    }
}

执行命令测试:

ab -c 11 -n 15 http://d2.maqian.co/master.zip # 同时产生11个连接,一共访问15次

nginx访问日志:

192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"

限制了同一时刻服务端只能有10个连接之后,现象也和上面的一样,15个连接有5个返回错误,但是不同的是这些连接可以同时来自于同一个IP。

1.3 多关键字配合使用

上面的基于IP和服务端访问限制可以同时使用,并且不只是这两个字段可以配合使用,其他的变量也都可以同时使用,具体的变量可以参考:Alphabetical index of variables

例如我们可以同时限制单个IP并发连接数为3,并且同时访问服务端的连接数为100:

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.maqian.co;
        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访问某个关键字维度的请求速率,其参数用法如下:

limit_req_zone key zone=name:size rate=rate;

以上的配置创建一个速率限制器,限制单个IP在key这个维度上的访问速率。和上面一样,这个key也通常被设置成访问者的IP。使用时在对应的server段内设置:

limit_req zone=name [burst=number] [nodelay];

burst表示令牌数量,连接满了之后,给接下来的连接发放令牌进行等待。令牌数量超出后,可以选择继续等待令牌或者直接返回错误状态。

这里的逻辑可以看成去银行办业务:人多的时候需要等号,number可以堪称最大的等号数量,rate可以堪称银行的窗口个数。银行同一时刻处理rate个客户的请求,并且同时允许number个客户排队,超出后,根据nodelay是否被设置来判断该连接是应该被丢弃还是等待。

参考文档:Module ngx_http_limit_req_module

2.1 限制每秒只处理同一个用户的一个请求

http {
    # 以请求IP作为KEY,设置访问频率为1秒1次请求
    limit_req_zone $binary_remote_addr zone=addr:10m rate=1r/s;
    server {
        listen       80;
        server_name  d2.maqian.co;
        # 设置队列为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;
    }
}

为了避免大文件下载耗时,这里不再和上面一样下载大文件,使用小文件测试:

ab -c 6 -n 100 http://d2.maqian.co # 6个并发连接,访问100次

得到日志后,复制到当前目录下,分别分析200和503响应的次数:

> grep "503" access.log | wc -l
94
> grep "200" access.log | wc -l
6

nginx第一秒处理第一个请求,同时给接下来的5个请求排队,剩下的都直接返回503,所以返回200的次数为6,503的次数为94。

ab返回的结果也能看到成功和失败的数量:

最后修改:2018 年 12 月 16 日
如果觉得我的文章对你有用,请随意赞赏