1.limit_req_zone

1.1 指令limit_req_zone的使用

# 定义一个以IP为限制请求的方式,名字为req_limit_zone,开辟10M的共享内存区域,每秒处理的速率为10个请求

limit_req_zone $binary_remote_addr zone=req_limit_zone:10m rate=10r/s;

说明 :limit_req_zone指令通常在 HTTP 块中定义,使其可在多个上下文中使用,它需要以下三个参数:

  • key - 定义应用限制的请求特性。示例中使用的是 Nginx 嵌入变量binary_remote_addr(二进制客户端地址)
  • zone - 定义用于存储每个 IP 地址状态以及被限制请求 URL 访问频率的共享内存区域。保存在内存共享区域的信息,意味着可以在 Nginx 的 worker 进程之间共享。定义分为两个部分:通过zone=keyword标识区域的名字,以及冒号后面跟区域大小。16000 个 IP 地址的状态信息,大约需要 1MB,所以示例中区域可以存储 160000 个 IP 地址。
  • rate - 定义最大请求速率。在示例中,速率不能超过每秒 10 个请求。Nginx 实际上以毫秒的粒度来跟踪请求,所以速率限制相当于每 100 毫秒 1 个请求。因为不允许”突发情况”,这意味着在距离前一个请求 100 毫秒内到达的请求将被拒绝。

1.2 指令limit_req的使用

limit_req zone=req_limit_zone burst=10 nodelay;

说明:

  • limit_req zone=req_limit_zone; 每个 IP 地址被限制为每秒只能请求 10 次 URL,更准确地说,在距离前一个请求的 100 毫秒内不能请求该 URL。
  • limit_req zone=req_limit_zone burst=10; burst 参数定义了超出 req_limit_zone指定速率的情况下(示例中的 req_limit_zone区域,速率限制在每秒 10 个请求,或每 100 毫秒一个请求),客户端还能发起多少请求。距离上一个请求 100 毫秒内到达的请求将会被放入队列,我们将队列大小设置为 10。

这意味着,如果从一个给定 IP 地址发送 11 个请求,Nginx 会立即将第一个请求发送到上游服务器群,然后将余下 10 个请求放在队列中。然后每 100 毫秒转发一个排队的请求,只有当传入请求使队列中排队的请求数超过 10 时,Nginx 才会向客户端返回 503。

配置 burst 参数将会使通讯更流畅,但是可能会不太实用,因为该配置会使站点看起来很慢。在上面的示例中,队列中的第 10 个包需要等待 1 秒才能被转发,此时返回给客户端的响应可能不再有用。

  • limit_req zone=req_limit_zone burst=10 nodelay;  使用 nodelay 参数,可以实现无延迟的排队;Nginx 仍将根据 burst 参数分配队列中的位置,当一个请求到达时,只要在队列中能分配位置,Nginx 将立即转发这个请求。将队列中的该位置标记为”taken”(占据),并且不会被释放以供另一个请求使用,直到一段时间后才会被释放(在这个示例中是,100 毫秒后)。

假设如前所述,队列中有 10 个空位,从给定的 IP 地址发出的 11 个请求同时到达。Nginx会立即转发这个 11 个请求,并且标记队列中占据的 10 个位置,然后每 100 毫秒释放一个位置。如果是15个请求同时到达,Nginx 将会立即转发其中的 11 个请求,标记队列中占据的 10 个位置,并且返回 503 状态码来拒绝剩下的 4 个请求。

2.limit_conn_zone

2.1 指令limit_conn_zone的使用

# 定义一个以IP为限制连接的方式,名字为conn_limit_zone,并开辟10m的共享内存区

limit_conn_zone $binary_remote_addr zone=conn_limit_zone:10m;

2.2 指令limit_conn的使用

limit_conn conn_limit_zone 2;

说明:

# 使用定义conn_limit_zone的连接限流,限制每个IP地址只允许同时创建2个连接

3.黑白名单

3.1 allow(白名单)、deny(黑名单) 方式

     参考:

location / {    deny  192.168.1.1;     #支持单个IP    allow 192.168.1.0/24;  #支持一个网段    allow 2001:0db8::/32;  #支持ipv6    deny all;   #支持all匹配到所有}

假如192.168.1.1 匹配到了上面的第1,2,4行,因为deny和allow是顺序匹配,匹配到即停止。所以第一条匹配到后,就直接deny了

3.2 ngx_http_geo_module 限流

参考:

geo $limit {    default         1;    10.0.0.0/8         0;    192.168.1.0/24     0;}map $limit $limit_key {    0 "";    1 $binary_remote_addr;}limit_req_zone $limit_key zone=geo_req_zone:10m rate=10r/s;server {    location /limit_demo {        limit_req zone=geo_req_zone burst=10 nodelay;        # ...    }}

这个例子同时使用了 geo 和 map 指令。geo 块将给在白名单中的 IP 地址对应的 $limit 变量分配一个值 0,给其它不在白名单中的分配一个值 1。然后我们使用一个映射将这些值转为 key,如下:

  • 如果变量的值是0,limit_key变量将被赋值为空字符串
  • 如果变量的值是1,limit_key变量将被赋值为客户端二进制形式的 IP 地址

两个指令配合使用,白名单内 IP 地址的$limit_key变量被赋值为空字符串,不在白名单内的被赋值为客户端的 IP 地址。当limit_req_zone后的第一个参数是空字符串时,不会应用“流量限制”,所以白名单内的 IP 地址(10.0.0.0/8 和192.168.1.0/24 网段内)不会被限制。其它所有 IP 地址都会被限制到每秒 10 个请求(每100毫秒1个请求)。

4. 发送到客户端的错误代码

可以使用limit_req_status指令来设置为其它状态码,默认503

5. 限流条件的扩展

除了上面用到的$binary_remote_addr ,其它常用的限流条件还包括:

 $binary_remote_addr    二进制的客户端地址,对于Ipv4,值的长度使用为4个字节,对于ipv6地址,值的长度始终为16个字节

 $server_name  接受请求的服务器的名称

 $cookie_name    cookie名

 $http_name  任意请求头字段,变量名的最后一部分是转换成小写的字段名,其中的破折号替换为下划线

 $realpath_root  与当前请求 的根或别名指令的值 相对应的绝对路径名 ,所有符号链接都解析为真实路径

 $remote_addr  客户地址

 $remote_user  随基本身份验证提供的用户名

 $request_uri  完整的原始请求 URI(带参数)

 $scheme  请求方案,“ http”或“ https”

 $uri 请求中的当前 URI

6. 完整的配置文件(nginx.conf):

#user  nobody;worker_processes  1;#error_log  logs/error.log;#error_log  logs/error.log  notice;#error_log  logs/error.log  info;#pid        logs/nginx.pid;events {    worker_connections  1024;}http {    include       mime.types;    default_type  application/octet-stream;    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '    #                  '$status $body_bytes_sent "$http_referer" '    #                  '"$http_user_agent" "$http_x_forwarded_for"';    #access_log  logs/access.log  main;    #限流配置开始************************************************************    #限流设置    limit_req_zone $binary_remote_addr zone=req_limit_zone:10m rate=10r/s;        #根据IP地址来限制,存储内存大小10M    limit_conn_zone $binary_remote_addr zone=conn_limit_zone:10m;    #geo限流    geo $limit {        default         1;        10.0.0.0/8         0;        192.168.1.0/32     0;    }    map $limit $limit_key {        0 "";        1 $binary_remote_addr;    }        limit_req_zone $limit_key zone=geo_req_zone:10m rate=10r/s;    #限流配置结束************************************************************    sendfile        on;    #tcp_nopush     on;    #keepalive_timeout  0;    keepalive_timeout  65;    #gzip  on;    server {        listen       80;        server_name  localhost;        #charset koi8-r;        #access_log  logs/host.access.log  main;        location / {            root   html;            index  index.html index.htm;        }          # 限流配置demo         location /limit_demo {            #allow配置白名单,支持一个网段或者单个地址            allow 192.168.1.0/24;            #deny配置黑名单,支持一个网段或者单个地址            deny 192.168.2.0/24;            #流量限制            limit_req zone=req_limit_zone burst=10 nodelay;            # 连接数限制             limit_conn conn_limit_zone 2;            # geo(白名单)流量限制(粒度更细一些)            limit_req zone=geo_req_zone burst=10 nodelay;            # 返回客户端错误代码            limit_req_status 503;            proxy_pass http://192.168.0.1:18081;        }        #error_page  404              /404.html;        # redirect server error pages to the static page /50x.html        #        error_page   500 502 503 504  /50x.html;        location = /50x.html {            root   html;        }        # proxy the PHP scripts to Apache listening on 127.0.0.1:80        #        #location ~ \.php$ {        #    proxy_pass   http://127.0.0.1;        #}        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000        #        #location ~ \.php$ {        #    root           html;        #    fastcgi_pass   127.0.0.1:9000;        #    fastcgi_index  index.php;        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;        #    include        fastcgi_params;        #}        # deny access to .htaccess files, if Apache's document root        # concurs with nginx's one        #        #location ~ /\.ht {        #    deny  all;        #}    }    # another virtual host using mix of IP-, name-, and port-based configuration    #    #server {    #    listen       8000;    #    listen       somename:8080;    #    server_name  somename  alias  another.alias;    #    location / {    #        root   html;    #        index  index.html index.htm;    #    }    #}    # HTTPS server    #    #server {    #    listen       443 ssl;    #    server_name  localhost;    #    ssl_certificate      cert.pem;    #    ssl_certificate_key  cert.key;    #    ssl_session_cache    shared:SSL:1m;    #    ssl_session_timeout  5m;    #    ssl_ciphers  HIGH:!aNULL:!MD5;    #    ssl_prefer_server_ciphers  on;    #    location / {    #        root   html;    #        index  index.html index.htm;    #    }    #}}