聊聊架构

使用Nginx实现限流

选择“ 置顶公众号 ”,精品文章第一时间送达!

在Web应用开发实践中,我们通常用Nginx作为接入层限流工具。接入层通常是指请求流量的入口。Nginx实现接入层限流主要依赖两个自带的模块,分别是连接数限流模块ngx_http_limit_conn_module和漏斗算法实现的请求限流模块ngx_http_limit_req_module。

1

按请求限流

limit_req是漏桶算法实现,用于对指定key对应的请求进行限流,比如,按照IP维度限制请求速率。配置示例如下:

 1http { 2   limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; 3   limit_conn_log_level error; 4   llimit_conn_status 503; 5   … 6   server { 7        location /limit { 8               limit_req zone=one burst=5 nodelay; 9        }10   }11}
  • limit_req:配置限流区域、桶容量(突发容量,默认为0)、是否延迟模式(默认延迟)。

  • limit_req_zone:配置限流key、存放key对应信息的共享内存区域大小、固定请求速率。此处指定的key是“$binary_remote_addr”,表示IP地址。固定请求速率使用rate参数配置,支持10r/s和60r/m,即每秒10个请求和每分钟60个请求。不过,最终都会转换为每秒的固定请求速率(10r/s为每100毫秒处理一个请求,60r/m为每1000毫秒处理一个请求)。

  • limit_conn_status:配置被限流后返回的状态码,默认返回503。

  • limit_conn_log_level:配置记录被限流后的日志级别,默认级别为error。

limit_req的主要执行过程如下:

(1)请求进入后首先判断最后一次请求时间相对于当前时间(第一次是0)是否需要限流,如果需要限流,则执行步骤(2),否则执行步骤(3)。

(2)如果没有配置桶容量(burst),则桶容量为0,按照固定速率处理请求。如果请求被限制,则直接返回相应的错误码(默认为503)。

如果配置了桶容量(burst>0)及延迟模式(没有配置nodely)。如果桶满了,则新进入的请求被限流。如果没有满,则请求会以固定平均速率被处理(按照固定速率并根据需要延迟处理请求,延迟使用休眠实现)。

如果配置了桶容量(burst>0)及非延迟模式(配置了nodely),则不会按照固定速率处理请求,而是允许突发处理请求。如果桶满了,则请求被限制,直接返回相应的错误码。

(3)如果没有被限流,则正常处理请求。

(4)Nginx会在相应时机选择一些(3个节点)限流key进行过期处理,进行内存回收。

2

按连接限流

limit_conn用来对某个key对应的总的网络连接数进行限流,可以按照如IP、域名维度进行限流,但是,记住不是每个请求连接都会被计数器统计,只有那些被Nginx处理的且已经读取了整个请求头的请求连接才会被计数器统计。

1、配置示例

 1http { 2   limit_conn_zone $binary_remote_add zone=addr:10m; 3   limit_conn_log_level error; 4   limit_conn_status 503; 5   … 6   server { 7        … 8        location /limit { 9               limit_conn addr 1;10        }11   }12   }

(1)limit_conn:要配置存放key和计数器的共享内存区域和指定key的最大连接数。此处指定的最大连接数是1,表示Nginx最多同时并发处理设置的key的1个连接。 (2)limit_conn_zone:用来配置限流key及存放key对应信息的共享内存区域大小。此处的key是“$binary_remote_addr”,表示IP地址,也可以使用$server_name作为key来限制域名级别的最大连接数。 (3)limit_conn_status:配置被限流后返回的状态码,默认返回503。 (4)limit_conn_log_level:配置记录被限流后的日志级别,默认error级别。

2、 limit_conn的主要执行过程 (1 )请求进入后首先判断当前limit_conn_zone中相应key的连接数是否超过了配置的最大连接数。 (2)如果超过了配置的最大大小,则被限流,返回limit_conn_status定义的错误状态码。否则相应key的连接数加1,并注册请求处理完的回调函数。 (3)进行请求处理。 (4)在结束请求阶段会调用注册的回调行数对相应key的连接数减1。

3、 按照IP限制并发连接数配置示例 首先,定义IP维度的限流区域:

1limit_conn_zone $binary_remote_addr zone=perip:10m;

接着在要限流的location中添加限流逻辑:

1location /limit {2   limit_conn perserver 2;3}

即允许每个IP最大并发连接数为2。 以上示例结论可以通过AB压力测试工具测试得出,可在Nginx的access log查看http status 503记录和error log里的Nginx报错信息。

4、按照域名限制并发连接数配置示例 首先,定义域名维度的限流区域:

1limit_conn_zone $server_name zone=perserver:10m;

接着在要限流的location中添加限流逻辑:

1location /limit {2   limit_conn perserver 2;3}

即允许每个域名最大并发请求连接数为2。这样配置可以实现服务器最大连接数限制。

3

设置限流白名单

限流一般主要针对来源于外部的请求,内网访问相对安全可信,可以不做限流,这时候可以通过设置白名单即可。Nginx 提供 ngx_http_geo_module 和 ngx_http_map_module 两个模块解决这个问题。

在 nginx.conf 的 http 部分中配置白名单如下:

 1geo $limit { 2    default 1; 3    10.0.0.0/8 0; 4    192.168.0.0/24 0; 5    172.16.30.73 0; 6} 7map $limit $limit_key { 8    0 ""; 9    1 $binary_remote_addr;10}11limit_req_zone $limit_key zone=myRateLimit:10m rate=10r/s;

(1)geo 对于白名单(子网或IP都可以) 将返回0,其他IP将返回1。 (2)map 将 $limit 转换为 $limit_key,如果是 $limit 是0(白名单),则返回空字符串;如果是1,则返回客户端实际IP。 (3)limit_req_zone 限流的key不再使用$binary_remote_addr,而是$limit_key =来动态获取值。如果是白名单,limit_req_zone 的限流key则为空字符串,将不会限流;若不是白名单,将会对客户端真实IP进行限流。

参考: (1)《亿级流量网站架构核心技术》

(2)Nginx官方博客:Rate Limiting with NGINX and NGINX Plus(https://www.nginx.com/blog/rate-limiting-nginx/)

(3)ngx_http_geo_module:http://nginx.org/en/docs/http/ngx_http_geo_module.html

(4)ngx_http_map_module:http://nginx.org/en/docs/http/ngx_http_map_module.html

▼
往期精彩回顾
▼
史上更全的MySQL高性能优化实战总结!
Linux里的2>&1究竟是什么
Linux中的jobs与nohup
[译] 基于事件流构建的服务
我们为什么要用HTTPS

·END·

聊聊架构

青春有限·艺无止境

微信号:ArchNote
更多精彩,点击下方“ 阅读原文 ”查看。