nginx限速

基础知识

map指令

map指令是ngx_http_map_module模块提供的。ngx_http_map_module模块用于创建值依赖于其他变量的值的变量。

示例配置:

map $http_host $name {  
    hostnames;

    default       0;

    example.com   1;
    *.example.com 1;
    example.org   2;
    *.example.org 2;
    .example.net  3;
    wap.*         4;
}

map $http_user_agent $mobile {  
    default       0;
    "~Opera Mini" 1;
}

指令

  • map
Syntax:    map string $variable { ... }  
Default:    —  
Context:    http  


map指令用于创建一个新的变量,它的值依赖于在第一个参数中指定的一个或多个源变量的值。
0.9.0之前的版本,在第一个参数中只可以指定一个变量
因为变量仅当它们被使用的时候才被计算,所以(甚至连大量的map变量的)纯粹的声明不会给请求处理添加额外的性能牺牲。
map块内部的参数指定了源值和结果值之间的映射。
源值被指定为字符串或正则表达式(0.9.6)。
正则表达式要么以“~”符号开始,用于大小写敏感的匹配,要么以“~*”(1.0.4)开始,用于大小写不敏感的匹配。
正则表达式可以包含命名分组和位置分组,这些分组和结果变量都能在其他指令中使用。
如果源值匹配下面将要描述的特殊参数(default,hostnames,include)中的任意一个,那么应该使用“\”作为前缀。
结果值可以是字符串,也可以是另一个变量(0.9.0)。

这个指令也支持三个特殊参数:
default value
当第一个参数不匹配任何源值的时候,将结果值设置为value。当没有指定default参数时,默认的结果值是空字符串。

hostnames
意味着源值可以是带有前缀或后缀掩码的主机名:

*.example.com 1;
example.*     1;  


下面的两个记录:

example.com   1;  
*.example.com 1;


可以合并成:

.example.com  1;


应该在值列表之前指定这个参数。

include file
包含带有值的文件,该参数可以指定多次。

当第一个参数匹配多个源值的时候,比如说:既匹配一个掩码,也匹配一个正则表达式,那么会按照下面的优先级序列进行选择:
1,不带掩码的字符串值。
2,带有前缀掩码的最长字符串值,比如:“*.example.com”。
3,带有后缀掩码的最长字符串值,比如:“mail.*”。
4,第一个匹配的正则表达式(在配置文件中出现的顺序)。
5,默认值。

  • map_hash_bucket_size
Syntax:    map_hash_bucket_size size;  
Default:    map_hash_bucket_size 32|64|128;  
Context:    http  


设置用于map变量的哈希表的桶的大小,默认值依赖于处理器的缓存,设置哈希表的细节是在一个单独的文档中提供的。

  • map_hash_max_size:
Syntax:    map_hash_max_size size;  
Default:    map_hash_max_size 2048;  
Context:    http  


设置用于map变量的哈希表的最大大小,默认值依赖于处理器的缓存,设置哈希表的细节是在一个单独的文档中提供的。

例子 map-example
example-1
example-2

注意

  • 结果值要么是字符串,要么是另外一个变量,不能是两个变量。
  • 当源值是正则表达式的时候,~,~*和正则表达式之间不能有空格。

geo指令

geo指令是由ngx_http_geo_module模块提供的,ngx_http_geo_module模块用于创建值依赖于客户端IP地址的变量。

示例配置:

geo $geo {  
    default        0;

    127.0.0.1      2;
    192.168.1.0/24 1;
    10.1.0.0/16    1;

    ::1            2;
    2001:0db8::/32 1;
}

指令

  • geo
Syntax:    geo [$address] $variable { ... }  
Default:    —  
Context:    http  


描述了(在客户端IP地址上)被指定的变量的值的依赖关系。默认情况下,地址从$remote_addr变量取,但是也可以从其他的变量取,比如:

geo $arg_remote_addr $geo {  
    ...;
}

因为变量仅当它们被使用的时候,才会被计算,所以(甚至大量的被声明的)geo变量的纯粹的存在,不会在请求处理过程中导致任何额外的性能开销。
如果变量的值不是合法的IP地址,那么“255.255.255.255”这个地址会被使用。
地址既可以被指定为CIDR标记法(包括单独的地址)中的前缀,也可以被指定为地址范围。
从1.3.10和1.2.7版本开始支持IPV6前缀
geo也支持下面的特殊的参数:
delete
删除指定的网络。

default
当客户端地址不匹配任何被指定的地址的时候,设置给变量的值。如果使用CIDR标记法指定地址,“0.0.0.0/0”和“::/0”能用于代替default。当没有指定default的时候,默认值是空字符串。

proxy
定义受信任的地址,当一个请求来自于受信任地址的时候,“X-Forwarded-For”请求头中的地址将会被使用,相比于一般的地址,受信任地址是顺序地检查的。
从1.3.0和1.2.1版本开始支持受信任的IPv6地址

proxy_recursive
开启递归查找地址。 如果关闭递归查找,在客户端地址与某个可信地址匹配时,nginx将使用“X-Forwarded-For”中的最后一个地址来代替原始客户端地址。如果开启递归查找,在客户端地址与某个可信地址匹配时,nginx将使用“X-Forwarded-For”中最后一个与所有可信地址都不匹配的地址来代替原始客户端地址(X-Forwarded-For的标准格式是---X-Forwarded-For: client1, proxy1, proxy2。) 。

ranges
意味着地址被指定为地址段(0.7.23)。这个参数应该是第一个参数。为了加速geo的加载,地址应该升序放置。

例子:

geo $country {  
    default        ZZ;
    include        conf/geo.conf;
    delete         127.0.0.0/16;
    proxy          192.168.100.0/24;
    proxy          2001:0db8::/32;

    127.0.0.0/24   US;
    127.0.0.1/32   RU;
    10.1.0.0/16    RU;
    192.168.1.0/24 UK;
}


conf/geo.conf文件可能包含下面的行:

10.2.0.0/16    RU;  
192.168.2.0/24 RU;  


最明确的匹配的值会被使用,比如对于地址127.0.0.1,值RU会被选择,而不是选择值US。
使用地址段的例子:

geo $country {  
    ranges;
    default                   ZZ;
    127.0.0.0-127.0.0.0       US;
    127.0.0.1-127.0.0.1       RU;
    127.0.0.1-127.0.0.255     US;
    10.1.0.0-10.1.255.255     RU;
    192.168.1.0-192.168.1.255 UK;
}

ngx_http_limit_conn_module模块

ngx_http_limit_conn_module模块用于限制每个定义的key的连接数量,尤其是限制来自于单个IP地址的连接数量。
并非所有的连接都会被该模块计数,只有那些正在处理的请求(同时,整个请求头已被读完)所在的连接才会被计数。
示例配置:

http {  
    limit_conn_zone $binary_remote_addr zone=addr:10m;

    ...

    server {

        ...

        location /download/ {
            limit_conn addr 1;
        }

指令

  • limit_conn_zone
Syntax:    limit_conn_zone key zone=name:size;  
Default:    —  
Context:    http  


设置保存多个key的状态的共享内存空间的参数。状态包含连接的当前数量,key能够包含文本,变量和两者的结合体。key为空值的请求不会被限制。
1.7.6以前的版本,key只能包含一个变量。

例子:
limit_conn_zone $binary_remote_addr zone=addr:10m;
在这个例子中,使用客户端IP地址作为key,注意在这里没有使用$remote_addr变量,而是使用了$binary_remote_addr变量。$remote_addr变量的长度为7到15个字节,因此在32位平台中占用32个字节或64个字节,在64位平台中占用64个字节。$binary_remote_addr变量的长度固定是4个字节,因此在32位平台中总是占用32个字节,在64位平台中占用64个字节。
1M共享空间可以保存大约3.2万个32位的状态或者大约1.6万个64位的状态。
如果共享内存空间被耗尽,服务器将会对后续所有的请求返回 503 (Service Temporarily Unavailable) 错误。

  • limit_zone
    limit_zone指令和limit_conn_zone指令作用相同,已经被弃用。

  • limit_conn

Syntax:    limit_conn zone number;  
Default:    —  
Context:    http, server, location  


设置使用的共享内存空间的名称,以及对于给定的key,所允许的最大连接数量。当限制达到的时候,服务器会返回503错误(Service Temporarily Unavailable)作为请求的响应。比如:

limit_conn_zone $binary_remote_addr zone=addr:10m;

server {  
    location /download/ {
        limit_conn addr 1;
    }


对于每个IP地址,同时只允许一个连接。
在HTTP/2和SPDY中,每个并发的请求被认为是一个单独的连接
当多个limit_conn指令被指定的时候,所有限制都会应用,比如下面的配置会限制每个客户端IP到服务器的连接数量,同时也会限制每个虚拟主机的总连接数量:

limit_conn_zone $binary_remote_addr zone=perip:10m;  
limit_conn_zone $server_name zone=perserver:10m;

server {  
    ...
    limit_conn perip 10;
    limit_conn perserver 100;
}


当且仅当当前层级没有limit_conn指令的时候,才会从上一层级继承。

  • limit_conn_log_level
Syntax:    limit_conn_log_level info | notice | warn | error;  
Default:    limit_conn_log_level error;  
Context:    http, server, location  
This directive appeared in version 0.8.18.  


设置当服务器限制连接数量的时候的日志级别。

  • limit_conn_status
Syntax:    limit_conn_status code;  
Default:    limit_conn_status 503;  
Context:    http, server, location  
This directive appeared in version 1.3.15.  


设置在拒绝的请求的响应中,返回的状态码。


ngx_http_limit_req_module模块

ngx_http_limit_req_module模块(0.7.21)用于限制每一个定义的key的请求处理速率,特别是限制来自于单个IP地址的请求处理速率。限制使用“漏桶”方法来完成。

示例配置:

http {  
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

    ...

    server {

        ...

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


指令

  • limit_req_zone
Syntax:    limit_req_zone key zone=name:size rate=rate;  
Default:    —  
Context:    http  


设置保存多个key的状态的共享内存空间的参数。状态存储了请求的当前数量。key能够包含文本,变量和二者的结合体。key值为空的请求不会被限制。
1.7.6以前的版本,key值只能包含一个变量。
例子:
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
这个例子中,状态被保存在10M的空间“one”中,并且这个空间的平均请求处理速率不能超过每秒一个请求。
在这个例子中,使用客户端IP地址作为key,注意:在这里没有使用$remote_addr变量,而是使用了$binary_remote_addr变量,这样做可以把状态大小降低到64个字节。1M空间能存储大约1.6万个64位的状态。
如果共享内存空间被耗尽,服务器将会对后续所有的请求返回 503 (Service Temporarily Unavailable) 错误。
速率以请求数/秒(r/s)的形式指定,如果想要设置一个每秒小于一个请求的速率,也可以以请求数/分钟(r/m)的形式指定速率。比如说每秒半个请求是30r/m。

  • limit_req
Syntax:    limit_req zone=name [burst=number] [nodelay];  
Default:    —  
Context:    http, server, location  


设置要使用的共享内存空间名称,以及请求的最大burst大小。如果请求的速率超过了配置的速率,它们的处理会被延迟,以便以定义的速率处理请求。超过频率限制的请求会被延迟,直到被延迟的请求数超过了最大burst大小,这时,请求会被终止,并返回503 (Service Temporarily Unavailable) 错误。默认请求下,最大burst大小等于0,比如下面的指令:

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

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


限制平均每秒不超过一个请求,同时允许超过频率限制的请求数不多于5个。
如果不希望超过频率限制的请求被延迟,可以用nodelay参数:
limit_req zone=one burst=5 nodelay;

  • limit_req_log_level
Syntax:    limit_req_log_level info | notice | warn | error;  
Default:    limit_req_log_level error;  
Context:    http, server, location  
This directive appeared in version 0.8.18  


设置由于超过频率限制,服务器拒绝处理请求或延迟处理请求时,期望的日志级别。延迟处理请求的日志级别比拒绝处理请求的日志级别低一级,比如说:如果limit_req_log_level notice被指定的话,延迟处理请求会以info级别被记录。

  • limit_req_status
Syntax:    limit_req_status code;  
Default:    limit_req_status 503;  
Context:    http, server, location  
This directive appeared in version 1.3.15.  


设置在拒绝的请求的响应中,返回的状态码。


ngx_http_core_module模块中限速相关的指令

  • limit_rate
Syntax:    limit_rate rate;  
Default:    limit_rate 0;  
Context:    http, server, location, if in location  


限制向客户端传送响应的速率。参数rate的单位是字节/秒,0表示关闭速率限制。 nginx按连接限速,所以如果某个客户端同时开启了两个连接,那么客户端的整体速率是这条指令设置的值的2倍。
也可以利用$limit_rate变量设置流量限制。$limit_rate对于想在特定条件下限制响应传输速率的情形非常有用:

server {

    if ($slow) {
        set $limit_rate 4k;
    }

    ...
}


此外,也可以通过“X-Accel-Limit-Rate”响应头来完成速率限制。 这种机制可以使用proxy_ignore_headers指令,fastcgi_ignore_headers指令,uwsgi_ignore_headers指令或scgi_ignore_headers指令来关闭。

  • limit_rate_after
Syntax:    limit_rate_after size;  
Default:    limit_rate_after 0;  
Context:    http, server, location, if in location  
This directive appeared in version 0.8.0.  


先全速传输size大小的响应后,再开始限速。
比如:

location /flv/ {  
    flv;
    limit_rate_after 500k;
    limit_rate       50k;
}

限速的例子

daemon off;  
events {  
    worker_connections 1024;
}

http {  
    limit_conn_zone $limit_c zone=channel_con:1024m;
    limit_req_zone  $limit_r zone=channel_req:1024m rate=20r/s;

    map $arg_channel $channel {
        default        $arg_channel;
        ~*^whitechannel "";
    }

    server {
        listen       8091;
        server_name  localhost;
        default_type text/plain;

        set $limit_c $channel;
        set $limit_r $channel;

        location / { 
            limit_conn channel_con 5;
            limit_req  zone=channel_req burst=3 nodelay;

            root   html;
            index  index.html index.htm;

            if ($arg_channel = '') {
                return 403 "Missing argument:`channel'";
            }   
            rewrite .* /index.html break;
        }   
    }   
}


在这个用于测试的例子中,无论访问哪一个path,都会返回nginx的欢迎页。并且需要客户端提供channel参数,如果没有提供的话,会返回403,同时会提示"Missing argument:`channel'"。
在这里,我们对channel做了区分,认为值以whitechannel头的channel是白名单用户,不去限制他们的并发连接数和请求处理速率;对于非白名单用户,每个channel的并发连接数不能超过5,并且每个channel每秒的请求不能超过20个,超出速率限制的请求会被延迟,延迟的请求数量不能超过3个,否则就会返回503。


参考资料

感谢浏览tim chow的作品!

如果您喜欢,可以分享到: 更多

如果您有任何疑问或想要与tim chow进行交流

可点此给tim chow发信

如有问题,也可在下面留言: