Nginx限制客户端的访问频次和访问次数

在生产应用过程中,nginx虽然可以承受住高并发,但是否这些并发连接都是有效的访问请求,还是恶意的访问?因此我们可以从限定客户端的访问频次和访问次数来使我们的nginx服务器来承受更高的有效并发。

cc攻击一般就是使用有限的ip数对服务器频繁发送数据来达到攻击的目的,nginx可以通过两个模块配置来限制ip在同一时间段的访问次数来防cc攻击。

先来介绍下nginx的两个模块:

1、NginxHttpLimitConnModule

限制单个IP的连接数,一段时间内的连接数限制

通过漏桶原理来限制单位时间内的请求数,一旦单位时间内请求数超过限制,就会返回 503 错误。

2、NginxHttpLimitReqModule

限制单个IP每秒请求数,同一时刻的连接数限制

NginxHttpLimitConnModule

http {
    limit_conn_zone $binary_remote_addr  zone=one:10m;
    server {
        limit_conn one 1;
        location =/1.html {
            root html;
        }
    }
}

1、limit_conn_zone $binary_remote_addr  zone=one:10m;

  • 第一个参数$binary_remote_addr :表示以客户端ip作为键值来进行限制,nginx 1.18以后用limit_conn_zone替换了limit_conn;
  • 第二个参数zone=one:10m:表示生成一个大小为10M,名字为one的存储区域,用来存储访问次数,1M能存储16000个状态;
  • 其他:limit_conn_zone $server_name zone=perserver:10m:$server_name是限制同一server最大并发数;

2、limit_conn one 1;

表示在one存储区内,限制客户端ip只能访问一次,若超过访问限制,则返回503错误。

我们用ab进行测试:ab -c 2 -n 10 http://www.test.cn/1.html

我们每次发起2个请求,一共有10次请求

[root@usvr-124 logs]# cat access.log

  1. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 200 0 0 235 90 0″-“
  2. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 503 0 0 371 90 0″-“
  3. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 200 0 0 235 90 0″-“
  4. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 503 0 0 371 90 0″-“
  5. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 200 0 0 235 90 0″-“
  6. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 503 0 0 371 90 0″-“
  7. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 503 0 0 371 90 0″-“
  8. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 200 0 0 235 90 0″-“
  9. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 503 0 0 371 90 0″-“
  10. 27/Nov/2018:11:25:30 +0800 – – 192.168.3.124:80 GET /1.html – – 192.168.3.126 HTTP/1.0 [ApacheBench/2.3] [-] – www.test.cn 200 0 0 235 90 0″-“

我们可以看到,每次访问是两个并发,每两个并发请求中有一个是被拒绝的,返回的是503错误。我们再来看下错误日志:

[root@usvr-124 logs]# cat error.log

  1. 2018/11/27 11:29:50 [error] 8445#0: *22317249 limiting connections by zone “one”, client: 192.168.3.126, server: www.test.cn, request: “GET /1.html HTTP/1.0”, host: “www.test.cn”
  2. 2018/11/27 11:29:50 [error] 8445#0: *22317251 limiting connections by zone “one”, client: 192.168.3.126, server: www.test.cn, request: “GET /1.html HTTP/1.0”, host: “www.test.cn”
  3. 2018/11/27 11:29:50 [error] 8445#0: *22317255 limiting connections by zone “one”, client: 192.168.3.126, server: www.test.cn, request: “GET /1.html HTTP/1.0”, host: “www.test.cn”
  4. 2018/11/27 11:29:50 [error] 8445#0: *22317257 limiting connections by zone “one”, client: 192.168.3.126, server: www.test.cn, request: “GET /1.html HTTP/1.0”, host: “www.test.cn”

从上面看出返回503错误的由于被我们设定的存储区域one所限制,也就是说我们设定的规则生效了。

NginxHttpLimitReqModule

http {
    limit_req_zone  $binary_remote_addr  zone=two:10m rate=5r/s;
    server {
        limit_req zone=two burst=5 nodelay;
        location =/1.html {
            root html;
        }
    }
}

1、limit_req_zone  $binary_remote_addr  zone=two:10m  rate=5r/s;

  • 第一个参数$binary_remote_addr:表示以客户端ip作为键值来进行限制;
  • 第二个参数zone=two:10m :表示生成一个大小为10M,名字为two的存储区域,用来存储访问频率;
  • 第三个参数 rate=5r/s:表示限定客户端的访问频率为每秒5次,rete的值必须为整数,速率在每秒请求中指定(r/s)。如果需要每秒少于一个请求的速率,则以每分钟的请求(r/m)指定,例如如果限制两秒钟一个请求,可以设置成30r/m;

2、limit_req zone=two burst=5 nodelay;

  • 第一个参数zone=two:表示使用存储区域two来限制
  • 第二个参数burst=5:表示设定一个缓存区域,当有大量请求时,超过了访问频次限制的请求会放在这个缓冲区域内
  • 第三个参数nodelay:漏桶原理也就是队列,表示当超过访问次数并缓冲也满的情况下,直接放回503错误,若不设置,这些多余的请求会延迟处理

用ab进行测试:ab -c 1 -n 5 -t 2 http://www.test.cn/1.html

由于在2秒内会请求很多次,因此在这就不把访问日志和错误日志贴出来了,大体我描述下:

  • 在第一秒内成功访问,也就是返回200的有6条记录
  • 在第二秒内成功访问,也就是返回200的有5条记录
  • 在第三秒内成功访问,也就是返回200的有4条记录

从上面看出,访问频次平均现在在5条左右,其实也没有精确的达到我们设置的限制,但是对外来说,这种效果还是起到作用了。

PS

  1. 看访问日志是限制没有精确达到我们要求的次数,但是不可否定的是这可以帮助我们的nginx节省部分流量,提高了可用的并发。
  2. 经过多次实验,limit_conn和limit_req都不是每次很准,有时差距和实际参数很大。