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; # } #}}
|