bigywater.tech
微服务 · 27

nginx 限流:limit_req 的漏桶与 burst、nodelay,limit_conn 与连接占用

limit_req 的 burst 和 nodelay 到底改变了什么?持续过载时放行多少?limit_conn 和 limit_req 守的是同一件事吗?这篇用 nginx 1.31.6 逐项量出来。

1 / 6 / 6
30 个请求同一时刻到达、rate=10r/s 时,无 burst / burst=5 / burst=5 nodelay 放行的数量;burst=5 的最慢一个等了 504 ms,nodelay 只要 10 ms
55
20 个/秒打 5 秒、rate=10r/s、burst 5 时共放行的数量(= 50 + burst 的 5 个);burst 0 只有 40
3 / 12
limit_conn 3 在 12 个并发请求里放行的数量,同样的请求 limit_req burst=20 nodelay 全部放行

burst 与 nodelay

实测 · 本机执行输出
  配置                   | 放行 | 拒绝(429) | 放行的请求各自的耗时(ms,从小到大)                
  ---------------------+----+---------+------------------------------------
  (a) 无 burst          | 1  | 29      | 4                                  
  (b) burst=5          | 6  | 24      | 6 107 202 305 405 504              
  (c) burst=5 nodelay  | 6  | 24      | 4 7 8 8 9 10                       
  (d) burst=10 delay=3 | 11 | 19      | 8 8 9 9 104 202 303 403 504 604 703
  无 burst 时同一时刻只放行 1 个;burst=5 放行 1 + 5 = 6 个,但后面 5 个被排队、每隔 100 ms 放一个(漏桶的“漏”);nodelay 让这 6 个立刻处理,代价是之后要隔够时间才再有额度;delay=3 是折中:前 1 + 3 个立刻,后面的排队延迟,总共 burst=10 个位置

burst 决定能多放几个,nodelay / delay 决定这些多放的请求排队还是立刻处理。

持续过载

实测 · 本机执行输出
  配置              | 100 个请求里放行 | 按发出时刻,第 0..4 秒各放行   
  ----------------+------------+---------------------
  burst 0         | 40         | [9, 8, 9, 7, 7]     
  burst 5         | 55         | [15, 10, 10, 10, 10]
  burst 5 nodelay | 55         | [15, 10, 10, 10, 10]
  速率上限就是 rate × 时间:5 秒 × 10 个/秒 = 50 个;burst 额外送一次性的额度(burst 5 最多多放 5 个,即 55);burst 0 时放行的比 50 还少一些,我的解释是请求每 50 ms 到一个,而放行要求距离上一个放行的请求至少 100 ms,到达时刻稍有抖动就差一点点被拒绝(这个解释没有单独验证);burst 排队放行的请求要等一会儿才有响应(漏桶每 100 ms 放一个),如果 client 是等响应再发下一个的(闭环),它自己的发送速率会被拖慢到 rate,于是一个都不会被拒绝,这里用开环才看得到拒绝

放行总量是 rate × 时间加 burst;闭环 client 会被排队拖慢到 rate,所以看不到拒绝。

limit_conn

实测 · 本机执行输出
  配置                                    | 放行 | 拒绝(429)
  --------------------------------------+----+--------
  limit_conn c 3                        | 3  | 9      
  limit_req rate=10r/s burst=20 nodelay | 12 | 0      

数的是同时在处理的请求,和 limit_req 数到达速率不同。

分步演示

nginx 限流
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。nginx 是 Homebrew 的 1.31.6;所有请求来自 127.0.0.1,限流 key 是 $binary_remote_addr,所以是同一个 key;后端是本进程里的 Python 服务;第一节用 barrier 让 30 个请求同时发出;第二节是开环 client;每个场景只测一次;我最初在第二节用的是顺序(闭环)client,burst 5 时一个都没被拒绝,因为排队放行拖慢了 client 自己,所以改成开环
    • 没有验证:多个 worker 进程之间限流计数的共享(要用 zone,这里是单 worker);limit_req_dry_run;limit_req 的 $limit_req_status;limit_req 作用于 HTTP/2 同一连接上的多个请求;按 XFF 取 key 时的 realip 先后顺序(见第 26 篇)
    京ICP备2026067502号-1京公网安备11010502063487号