Nginx 限流模块:限制高并发和IP访问频率

时间:2022-10-10 08:51:23

Nginx 是我们常用的负载均衡和反向代理服务器,并发性能非常优秀。

但是在并发量极大的情况下,必要限流措施还是需要的,Nginx 的有对应的模块插件可通过简单配置来完成这个功能。
Nginx 限流模块:限制高并发和IP访问频率

限制并发,限制ip并发数,也是说限制同一个ip同时连接服务器的数量

1、添加limit_conn_zone
这个变量只能在http使用。

http{
...
#定义一个名为one的limit_zone,大小10M内存来存储session,
#以$binary_remote_addr 为key
#nginx 1.18 以后用limit_conn_zone替换了limit_conn
#且只能放在http作用域
limit_conn_zone $binary_remote_addr zone=one:10m;

2、添加limit_conn

这个变量可以在http, server, location使用。只限制一个站点,所以添加到server里面。
server{
...
location {
...
limit_conn one 20; #连接数限制
#带宽限制,对单个连接限数,如果一个ip两个连接,就是500x2k
limit_rate 500k;
...
}
...
}

限制IP访问频率

限制同一个ip在一段时间里连接服务器的次数,可以一定程度上防止类似CC这种快速频率请求的攻击。
1、添加limit_req_zone

这个变量只能在http使用
http{
...
#定义一个名为allips的limit_req_zone用来存储session,大小是10M内存,
#以$binary_remote_addr 为key,限制平均每秒的请求为5个,
#1M能存储16000个状态,rete的值必须为整数,
#如果限制两秒钟一个请求,可以设置成30r/m
limit_req_zone $binary_remote_addr zone=allips:10m rate=5r/s;
...

2、添加limit_req
这个变量可以在http, server, location使用。只限制一个站点,所以添加到server里面。
3、重启Nginx。
上面两种配置,注意看注释,比较实用。

limit_req的配置举例

1、常用的两个指令:

nginx.conf

limit_req_zone:定义到http区域:

limit_req_zone $binary_remote_addr zone=perip:20m rate=1r/s;

limit_req:定义到server或location区域

limit_req zone=perip;

说明:

变量:
$binary_remote_addr,这里是客户端的ip地址,
在这里是做限制的标识,是基于ip地址来限制
zone=perip:20m: perip是内存区域的名字,
20m: 生成的内存区域的大小
rate=1r/s: 允放相同标识的客户端的访问频次,
在这个例子中:就是同一个ip地址在每秒内只能访问1次

2、例子一:以每秒处理1个请求的速率做限制

limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s;
...
limit_req zone=perip;

重启 nginx后测试:

$ ab -c 10 -n 10 http://www.xxx.com/index.html
...
Concurrency Level:      10
Time taken for tests:   0.002 seconds
Complete requests:      10
Failed requests:        9
   (Connect: 0, Receive: 0, Length: 9, Exceptions: 0)
Non-2xx responses:      9
...

只有一个成功,有9个请求失败
时长是0.002 seconds

3、例子2:burst

增加burst,再次测试
limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s;
...
limit_req zone=perip burst=2;

说明:
burst=2 允许2个突发,
有大量请求时,超过频次限制的请求,会允许2个访问,
注意:burst指定的请求数量,不会马上进行处理,
而是按照rate指定的值,以固定的速率进行处理。
以10个并发请求为例:
rate= 1r/s
就是先处理1个,剩下的9个中,
7个请求,直接返回503
剩下的2个放到突发的队列中延迟执行,
仍然按rate= 1r/s进行处理,所以需要约两秒的等待时间

$ ab -c 10 -n 10 http://www.xxx.com/index.html
...
Concurrency Level:      10
Time taken for tests:   2.004 seconds
Complete requests:      10
Failed requests:        7
   (Connect: 0, Receive: 0, Length: 7, Exceptions: 0)
Non-2xx responses:      7
...

有7个请求失败

注意时间变成了2.004 seconds

4、例子三:nodelay

limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s;
...
limit_req zone=perip burst=2 nodelay;

说明:
nodelay参数:
burst的队列虽然可以处理用户的需求,但需要用户按照处理时间等待,对用户不够友好,
nodelay参数允许请求在排队的时候就立即被处理,
这里有一点要注意:因为nodelay允许立即处理,也就是有并发请求时
事实上已经超过了rate设置的处理速率了,
所以要根据自己机器的实际情况设置这个值

理解nodelay:
nodelay只是对放到burst队列中的请求立即处理,
但处理完成后队列并不立即清空,
队列清空的速度仍然按原来的速度每秒一个清空,
所以当再有请求过来时,并不会马上又有两个burst请求被处理.

再次测试

$ ab -c 10 -n 10 http://www.xxx.com/index.html
...
Concurrency Level:      10
Time taken for tests:   0.002 seconds
Complete requests:      10
Failed requests:        7
   (Connect: 0, Receive: 0, Length: 7, Exceptions: 0)
Non-2xx responses:      7
...

有7个请求失败

注意时间:这次是0.002 seconds

rate和burst应该设置为多少?

1、网站需要把动态生成的内容(java,php,python等程序生成)和静态内容分离到不同的虚拟主机

因为静态内容不需要大量的计算,
通常limit_req用于限制动态内容的访问频率

2、限流的目的不是让用户不能访问,

而是为了保证用户能流畅的访问,
所以需要根据页面的实际情况来限制
如果一个页面打开时同时发出的请求比较多,
(静态文件分离后要检查ajax请求数,
可以根据生产环境的日志进行检查)
则rate值不能低于并发的请求数.
如果低于并发的情求数,会导致用户不能正常访问页面

我们在生产环境中的例子,仅供参考

limit_req_zone $binary_remote_addr zone=reqperip:20m rate=20r/s;
limit_req zone=reqperip burst=10 nodelay;

其他可配置的参数:

1、limit_req_status用来指定请求时报错产生的状态码:

limit_req_status 这个值默认是503,
可以指定为一个自定义的值,

例如:444
说明:444是nginx自定义的一个非标准状态码
      它会立即关闭连接,连响应头也不给客户端发
      可以在受到恶意攻击时使用这个状态码
2、limit_req_log_level   当报错时记录到日志的错误级别,

默认值是error
可选值: info | notice | warn | error
不建议改动这个值

参考:文章
文章