使用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
·END·
青春有限·艺无止境