美文网首页
NGINX限流

NGINX限流

作者: 夜清溟 | 来源:发表于2023-03-01 16:13 被阅读0次

    Nginx 限流

    注意: 通常情况下,将限速模块用于转发到后端接口的时候,每个IP每秒请求的次数可以设置的小一些,比如10-20次请求/每秒,如果用于前端静态资源的话,由于加载一次前端页面,会同时请求很多的静态资源等等,瞬间的请求数量可能达到数百个,因此需要根据情况将每个IP每秒请求次数加大。

    Nginx 提供两种限流方式,一是控制速率,二是控制并发连接数。

    控制速率

    正常限流

    ngx_http_limit_req_module 模块提供限制请求处理速率能力,使用了漏桶算法(leaky bucket)。下面例子使用 nginx limit_req_zone 和 limit_req 两个指令,限制单个IP的请求处理速率。

    nginx.conf http 中添加限流配置:

    格式:limit_req_zone key zone rate

    http {
        limit_req_zone $binary_remote_addr zone=myRateLimit:10m rate=10r/s;
    }
    

    配置 server,使用 limit_req 指令应用限流。

    server {
        location /  {
            limit_req zone=myRateLimit;
            proxy_pass http://my_upstream;
        }
    }
    

    key :定义限流对象,binary_remote_addr 是一种key,表示基于 remote_addr(客户端IP) 来做限流,binary_ 的目的是压缩内存占用量。
    zone:定义共享内存区来存储访问信息, myRateLimit:10m 表示一个大小为10M,名字为myRateLimit的内存区域。1M能存储16000 IP地址的访问信息,10M可以存储16W IP地址访问信息。
    rate 用于设置最大访问速率,rate=10r/s 表示每秒最多处理10个请求。Nginx 实际上以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制:每100毫秒处理一个请求。这意味着,自上一个请求处理完后,若后续100毫秒内又有请求到达,将拒绝处理该请求。

    处理突发流量

    上面例子限制 10r/s,如果有时正常流量突然增大,超出的请求将被拒绝,无法处理突发流量,可以结合 burst 参数使用来解决该问题。

    server {
        location /  {
            limit_req zone=myRateLimit burst=20;
            proxy_pass http://my_upstream;
        }
    }
    

    burst 译为突发、爆发,表示在超过设定的处理速率后能额外处理的请求数。当 rate=10r/s 时,将1s拆成10份,即每100ms可处理1个请求。

    此处,**burst=20 **,也就是说如果有22个请求同时发送过来,那么NGINX会马上把第1个请求根据相关规则转发给upstream服务器,然后把接下来的第2到21共计20个请求放入队列中,接着直接返回503代码给第22个请求,随后的2秒时间内,每100毫秒从队列中取出一个请求发送给upstream服务器进行处理。

    不过,单独使用 burst 参数并不实用。假设 burst=50 ,rate依然为10r/s,排队中的50个请求虽然每100ms会处理一个,但第50个请求却需要等待 50 * 100ms即 5s,这么长的处理时间自然难以接受。

    因此,burst 往往结合 nodelay 一起使用。

    server {
        location /  {
            limit_req zone=myRateLimit burst=20 nodelay;
            proxy_pass http://my_upstream;
        }
    }
    

    nodelay 针对的是 burst 参数,burst=20 nodelay 表示这20个请求立马处理,不能延迟,相当于特事特办。不过,即使这20个突发请求立马处理结束,后续来了请求也不会立马处理。burst=20 相当于缓存队列中占了20个坑,即使请求被处理了,这20个位置这只能按 100ms一个来释放。

    加上了nodelay参数之后,nginx的处理方式和上面基本相同,唯一的区别在于:当nginx接受了第2到21共计20个请求之后,不会把它们放入队列中,而是直接将它们转发给upstream服务器,同时标记队列中的这20个插槽(slot)为已使用,然后把剩下的全部请求都503拒绝掉,接着每过100毫秒再释放一个新的slot让新的请求进来。

    With the nodelay parameter, NGINX still allocates slots in the queue according to the burst parameter and imposes the configured rate limit, but not by spacing out the forwarding of queued requests.

    Instead, when a request arrives “too soon”, NGINX forwards it immediately as long as there is a slot available for it in the queue. It marks that slot as “taken” and does not free it for use by another request until the appropriate time has passed (in our example, after 100ms).

    现在假设在第一组请求转发后 101 毫秒,另外 20 个请求同时到达。队列中只有 1 个插槽已被释放,因此 NGINX 转发 1 个请求给upstream服务器并以 status 503 拒绝其他 19 个请求。如果在 20 个新请求到达之前 已经过去了501毫秒而不是101毫秒,则有 5 个空闲槽,因此 NGINX 立即转发 5 个请求upstream服务器并拒绝剩余15 个请求。这样最终的效果相当于每秒 10 个请求的速率限制,只不过没有了前面的“均匀平滑”的特性,但是却有效降低了响应时间。因此如果我们需要在不限制每个请求之间的时间间隔的情况下限制请求速率,可以考虑使用nodelay参数。

    Note: For most deployments, we recommend including the burst and nodelay parameters to the limit_req directive.

    这就达到了速率稳定,但突然流量也能正常处理的效果。

    限制连接数

    ngx_http_limit_conn_module 提供了限制连接数的能力,利用 limit_conn_zonelimit_conn 两个指令即可。下面是 Nginx 官方例子:

    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 perip 10 作用的key 是 $binary_remote_addr,表示限制单个IP同时最多能持有10个连接。

    limit_conn perserver 100 作用的key是 $server_name,表示虚拟主机(server) 同时能处理并发连接的总数。

    需要注意的是:只有当 request header 被后端server处理后,这个连接才进行计数。

    设置白名单

    限流主要针对外部访问,内网访问相对安全,可以不做限流,通过设置白名单即可。利用 Nginx ngx_http_geo_modulengx_http_map_module 两个工具模块即可搞定。

    nginx.confhttp 部分中配置白名单:

    geo $limit {
        default  1;
        10.0.0.0/8  0;
        192.168.0.0/24  0; 
        172.20.0.35  0;
    }
    
    map $limit $limit_key {
        0  "";
        1 $binary_remote_addr;
    }
    
    limit_req_zone $limit_key zone=myRateLimit:10m rate=10r/s;
    

    geo 对于白名单(子网或IP都可以) 将返回0,其他IP将返回1。

    map$limit 转换为 $limit_key,如果是 $limit 是0(白名单),则返回空字符串;如果是1,则返回客户端实际IP。

    limit_req_zone 限流的key不再使用 binary_remote_addr**,而是 **limit_key 来动态获取值。如果是白名单,limit_req_zone 的限流key则为空字符串,将不会限流;若不是白名单,将会对客户端真实IP进行限流。

    拓展阅读

    除限流外,ngx_http_core_module 还提供了限制数据传输速度的能力(即常说的下载速度)。

    例如:

    location /flv/  {
        flv;
        limit_rate_after 20m;
        limit_rate 100k;
    }
    

    这个限制是针对每个请求的,表示客户端下载前20M时不限速,后续限制100kb/s。

    相关文章

      网友评论

          本文标题:NGINX限流

          本文链接:https://www.haomeiwen.com/subject/whfdldtx.html