1.产生背景

今天上午上班突然接到同时请求,需要对Nginx做个限流,因为我们业务的特性,限流的确比把ip加入黑名单强,那么接下来就开始我的研究限流之旅。

这里先简单说下什么是限流?就是对用户访问你服务器的请求数或者连接数做个限制,不能像以前那样畅所欲为!

2.Nginx实现限流的方式

2.1 模块介绍

 Nginx特性是模块化,就由ngx_http_limit_conn_module及ngx_http_limit_req_module 2个模块组合起提供限流功能,不需要额外安装,属于Nginx内置模块,只要您安装Nginx就可以通过此模块的指令调用此功能。

ngx_http_limit_conn_module:主要用于设置用户并发连接数,一般用于服务器流量异常、负载过大,甚至是大流量的恶意攻击访问等场景。

ngx_http_limit_req_module:主要用于单个ip限流,限制单个ip的请求数,一般用于防止应用层的dos攻击,也可以被限制黑名单方式代替。

2.2 模块指令实战

1).设置最大并发数

#设置共享内存区域和允许的最大并发数。当超过此限制时,服务器将返回错误以响应请求。由ngx_http_limit_conn_module模块提供此功能。
指令:limit_conn_zone  限制共享区域大小
语法:limit_conn_zone $variable zone=name:size;    #zone = name定义区域名称,size是各个键共享内存空间大小,主要存储session会话的,官方默认值是10M

#限制并发连接数,达到真正的限速目的是由此指令提供的
语法:limit_conn zone number  #zone是limit_conn_zone定义的区域名称,number就是具体的并发连接数
contest:http,server,location

#当达到最大限制连接数后,记录日志的等级,会输出到Nginx错误日志中。    
语法:limit_conn_log_level info|notice|warn|error; 
默认值:limit_conn_log_leve error;
生效区间:http,server,location

#当达到最大限制连接数后,向客户端返回特定的错误码
语法:limit_conn_status code;
默认值:limit_conn_status 503;
生效范围:http,server,location

示例如下:

示例:
limit_conn_zone $binary_remote_addr zone=addr:10m;  #这里是设置共享内存空间
limit_conn_log_level error;  #这是指错误日志记录级别(加不加影响不大)
limit_conn_status 503; #返回503状态(针对超过限制值的请求)

server {
    location /download/ {
    limit_conn addr 1;       #设置最大并发数,也就是当客户端同时发起2个访问时,就返回503
}

实例:
#限制最大并发数
root@test:/usr/local/nginx/conf/vhost 18:28:50 
$ cat ../nginx.conf | grep limit | grep -v "^wor" | egrep  -v '#'
limit_conn_zone $binary_remote_addr zone=one:10m;

$ cat  7250.conf 
server {
    listen 80;
    server_name ab.text.com;
......

    limit_conn one 2;      #对并发进行限制,一般放在server字段或者location字段。
    #limit_rate_after 80M;    #指定在发送多少数据后就进行限速
    #limit_rate 600k;    #limit_rate指定每秒返回的字节数

......

$ cat /data/www/index.php    #测试文件,以动态文件为主,静态文件获取太快,很难得到结果
<?php 
        echo 1;
        usleep(50000);
?>


#测试(默认是平滑加载Nginx.conf了) 
$ ab -c 2  -n 9  ab.text.com/index.php                  # -c 是指并发数 -n是请求数
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 ab.text.com (be patient).....done

Server Software:        nginx
Server Hostname:        ab.text.com
Server Port:            80

Document Path:          /index.php
Document Length:        2 bytes

Concurrency Level:      2              #我总共发起了2个并发,所以并发数为2
Time taken for tests:   0.036 seconds
Complete requests:      9              #我总共发起了9个请求,这里完成请求数就为9
Failed requests:        0              #失败为0,在2个并发下发起9个请求,完全被处理


$ ab -c 3  -n 9  ab.text.com/index.php
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 ab.text.com (be patient).....done

Server Software:        nginx
Server Hostname:        ab.text.com
Server Port:            80

Document Path:          /index.php
Document Length:        2 bytes

Concurrency Level:      3  #发起3个并发,比我设置的值多一个并发
Time taken for tests:   0.014 seconds
Complete requests:      9  #还是完成9个请求
Failed requests:        6  #但是失败6个请求,如果在php5.6的话应该是失败3个请求,
                           7的话不知道为啥失败6个请求(我们目的是研究并发,php问题先放放)

根据上述结果:limit_conn one指令对并发的限制是非常明显的

2).对单个ip进行限流

ngx_http_limit_req_module是Nginx的默认编译模块,主要用限制每个客户端每秒的请求数简单来说是限流。只是表现在对请求次数的限制,但是并非所有连接都被计数。只有被服务器处理的用户请求连接,才会被进行计数。
涉及算法: leaky bucket算法,又熟称漏桶算法。

简单来说,根据ngx_http_limit_req_module进行限流后,可以把突发的流量以一个平稳的速度进行数据传输,但是也会出现相应的情况那就是用户响应变慢。

原理:首先在内存中创建一个内存共享存储池,也就是下面那个大碗,然后将用户的请求都存储到这个碗里,在然后按照既定的速度对用户请求进行处理返回响应。如果这个大碗满了,Nginx将返回错误码并且拒绝接收用户请求。

ngx_http_limit_req_module4个指令如下:

#定义共享内存,key关键字及限制速率
语法:limit_red_zone key zone=name:size rate=rates/; #key 关键字 zone创建缓存空间 rate设置速率
生效范围:http
rate的单位是r/s或者r/m,是指每分钟能处理多少个请求。r对应的是请求数

#限制并发连接数
语法:limit_red zone = name [burst=number][nodelay];
生效范围:http,server,location
burst默认值为0,就那个大碗
设置了nodelay,就会对burst中的请求不在采用延迟处理,而是立即返回错误。

#达到最大上限值记录日志级别
语法:limit_red_log_lever info|notice|warn|error;
默认值:limit_red_log_level error;
生效范围:http,server,location

#达到最大上限值,向客户端返回特定的错误码
语法:limit_red_status code;
默认值:limit_red_status 503;
生效范围:http,server,location


示例如下:

示例:
http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    limit_red_log_level error
    limit_red_status 503

    ...

    server {

        ...

        location /search/ {
            limit_req zone=one burst=5;  
        }

实例:
$ cat ../nginx.conf | grep limit | grep -v '^worker'
    limit_req_zone $binary_remote_addr zone=name:10m rate=10r/s; #
    
     #rate=10r/s  是指每s只能处理10个请求,你也可以设置为 rate=20r/s,30..等等,后面是数字代表请求数,其他的默认就好。



$ cat  7250.conf  
server {
    listen 80;
    server_name ab.text.com;
......

    limit_req zone=name burst=5;  #burst 指的是突发数
.....

}

注:测试文件还是上面那个index.php

测试:
$ ab -n 100 ab.text.com/index.php
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 ab.text.com (be patient).....done

Server Software:        nginx
Server Hostname:        ab.text.com
Server Port:            80

Document Path:          /index.php
Document Length:        2 bytes

Concurrency Level:      1
Time taken for tests:   9.907 seconds #这里耗时9.9s,也就是说1s只处理了10个请求,达到预期
Complete requests:      100     #完成请求100个,跟我们期望值一样
Failed requests:        0

3).额外的限流指令 那就是 limit_rate 指令了,可以加载server,location,http字段的话没有试过!

limit_rate_after number,也就是对这个值不进行限速

limit_rate  number,也就是对超过limit_rate_after值的流量进行限速。

起来是不是感觉有点拗口,别怕,直接上实例说明:

实例:
$ cat 7250.conf
server {
    listen 80;
    server_name ab.text.com;
......

    limit_rate_after 90M;    #这个是指对前90M内容不限速
    limit_rate 600k;   #这个是指对90M之后的内容限速为600Kb每S

......
}

废话不多说,直接来个下载瞧瞧

浏览器访问:ab.text.com/jdk1 

注:是不是在前面快的飞起,达到10M/S,然后后面是只有500多KB无限接近600KB/S,那就代表达到我们的理想效果。

关于限流的知识就介绍到这里,还有个worker_connections限制文件打开数的话,不知道怎么测试就在这里不说了,当然欢迎各位看官留言讨论。

参考文章:

https://baike.so.com/doc/2372586-2508646.html

https://baike.so.com/doc/1712488-1810506.html

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐