nginx限流

Nginx 按请求速率限速模块使用的是漏桶算法,即能够强行保证请求的实时处理速度不会超过设置的阈值。

相关指令

limit_req_zone

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
  • $binary_remote_addr 表示通过客户端 IP 来进行限制,“binary_”的目的是缩小内存占用量。

    限制的是同一客户端 ip 地址的流量。

  • zone=one:10m 表示生成一个大小为 10M,名字为 one 的内存区域,用来存储访问的频次信息。

  • rate=1r/s 表示允许相同标识的客户端的访问频次,这里限制的是每秒 1 次。

limit_req

limit_req zone=one burst=5 nodelay;
  • zone=one 设置使用哪个配置区域来做限制,与上面 limit_req_zone 里的 zone=one:10m 对应。

  • burst=5 burst 是爆发的意思,这个配置的意思是设置一个大小为 5 的缓冲区,当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内。

  • nodelay,如果添加了该属性,超过访问频次且缓冲区也满了的时候就会直接返回503;如果没有添加,则所有请求会等待排队。

示例

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    server {
        location /search/ {
            limit_req zone=one burst=5 nodelay;
        }
}        

限制特定 UA(比如搜索引擎)的访问:

limit_req_zone  $anti_spider  zone=one:10m   rate=10r/s;
limit_req zone=one burst=100 nodelay;
if ($http_user_agent ~* "googlebot|bingbot|Feedfetcher-Google") {
    set $anti_spider $http_user_agent;
}

实战

实例一:限制访问速率

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit;
    }
}

上述规则限制了每个 IP 访问的速度为 2r/s,并将该规则作用于根目录。

如上图所示,使用单个 IP 在 10ms 内发并发送了 6 个请求,只有 1 个成功,剩下的 5 个都被拒绝。

设置的速度是 2r/s,为什么只有 1 个成功呢?

因为 Nginx 的限流统计是基于毫秒的,2r/s 转换一下就是 500ms 内单个 IP 只允许通过 1 个请求,从501ms 开始才允许通过第二个请求。

实例二:burst缓存处理

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4;
    }
}

上面的配置中加入了 burst=4,意思是每个 key(此处是每个 IP)最多允许 4 个突发请求的到来。

相比实例一成功数增加了 4 个,这与设置的 burst 数目是一致的。

具体处理流程是:1 个请求被立即处理,4 个请求被放到 burst 队列里,另外一个请求被拒绝。通过burst参数,使得 Nginx 限流具备了缓存处理突发流量的能力。

burst 的作用是让多余的请求可以先放到队列里,慢慢处理。如果不加 nodelay 参数,队列里的请求不会立即处理,而是按照 rate 设置的速度,以毫秒级精确的速度慢慢处理。

实例三:nodelay降低排队时间

nodelay 参数允许请求在排队的时候就立即被处理,也就是说只要请求能够进入 burst 队列,就会立即被后台 worker 处理,这意味着 burst 设置了 nodelay 时,系统瞬间的 QPS 可能会超过 rate 设置的阈值。

nodelay 参数要跟burst一起使用才有作用。

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4 nodelay;
    }
}

单个 IP 10ms 内并发发送 6 个请求,结果如下:

跟实例二相比,请求成功率没变化,但是总体耗时变短了。

  • 实例二中,有 4 个请求被放到 burst 队列当中,工作进程每隔 500ms(rate=2r/s)取一个请求进行处理,最后一个请求要排队 2s 才会被处理;

  • 实例三中,请求放入队列跟实例二是一样的,但不同的是,队列中的请求同时具有了被处理的资格,所以实例三中的 5 个请求可以说是同时开始被处理的,花费时间自然变短了。

虽然设置 burst 和nodelay 能够降低突发请求的处理时间,但是长期来看并不会提高吞吐量的上限,长期吞吐量的上限是由 rate 决定的,因为 nodelay 只能保证 burst 的请求被立即处理,但 Nginx 会限制队列元素释放的速度

Last updated