limit_req通过毫秒级时间差批量“补漏”模拟漏桶,需精准设key粒度、合理配burst/nodelay、足量共享内存zone,并配合limit_conn与业务层限流。

直接用 limit_req 构建平流限流体系,核心不是“堆参数”,而是理解它怎么在毫秒级离散判断中模拟漏桶——它不匀速滴水,而是在每个请求到达时,按时间差批量“补漏”再扣减。这种机制天然适合接入层做节奏控制,但必须配合 key 粒度、burst 设计和多级协同,才能真正压平流量尖峰。
精准定义限流粒度:key 决定谁被限、谁被误伤
key 是限流生效的锚点,选错就等于防护失效或误杀正常用户:
- 单 IP 限流用
$binary_remote_addr最稳妥,二进制存储节省内存;但要注意 NAT 场景(如企业出口、校园网),多个用户共用一个公网 IP,此时可叠加$http_user_agent或$http_x_forwarded_for(需可信代理)来粗略区分设备 - 接口级限流可组合变量,例如登录接口用
$binary_remote_addr$http_uri,避免同一 IP 刷不同路径互相干扰 - 业务级限流建议加标识字段,比如会员等级、APP 版本号,便于后续做灰度配额
burst 与 nodelay 的组合逻辑:缓冲 ≠ 放行,配额 ≠ 加速
burst 不是提升吞吐的“油门”,而是给漏桶加的临时缓冲区;nodelay 也不是取消限流,只是把 burst 当作瞬时额度提前兑现:
- 若要平抑短时毛刺(如页面 onload 触发多个资源请求),用
burst=5 nodelay:前 6 个请求(1 基础 + 5 burst)全部立即通过,第 7 个开始严格按间隔放行 - 若要保障长尾体验(如上传类接口),去掉
nodelay,让超额请求排队等待:rate=2r/s、burst=10 时,第 11 个请求要等满 5 秒才开始处理,避免后端瞬时积压 - burst 值不宜过大,超过 20 容易导致延迟不可控;超过 50 基本失去“平流”意义,接近无限制
共享内存 zone 必须够用:内存不足 = 限流失效
所有计数器存在共享内存里,worker 进程共用。zone 太小会导致旧节点被强制淘汰,新请求找不到历史记录,相当于从零开始计数:
- 1MB zone 约支持 1.6 万个独立 key(IP 或组合 key)
- 预估日活 IP 数 × 1.5(考虑爬虫、重试等),再乘以 1.2 的冗余系数,就是最小 zone 大小。例如 30 万活跃 IP,至少配
zone=req_ip:32m - 可在 Nginx 日志中加
$limit_req_status变量,观察是否频繁出现miss(key 未命中,可能因 zone 满被挤出)
必须搭配 limit\_conn 和业务层限流:漏桶只管节奏,不管连接和逻辑
纯 limit_req 对慢连接攻击、长耗时请求、恶意构造的合法请求无效:
- 用
limit_conn控制单 IP 并发连接数,防慢速 HTTP 攻击(如 Slowloris) - 对登录、下单等关键路径,在应用层做二次校验,比如 Redis 记录 5 分钟内失败次数,超限则返回验证码或锁定
- 前端加防抖(debounce)和节流(throttle),从源头减少无效请求,比后端拦截更省资源










