ByteNoteByteNote
Nginx 请求限流实战:场景、参数与最佳实践
字

字节笔记本

2026年10月6日 · 约 9 分钟读完

Nginx 请求限流实战:场景、参数与最佳实践

API中转
¥120

在高并发场景下,请求限流是保护服务的第一道闸门。突发流量一旦涌入,缺少限流手段的服务往往依次出现负载飙升、响应变慢、正常用户被挤出,最坏情况下整台服务宕机。Nginx 自带的 limit_req 模块用几十行配置就能把这波洪峰挡在门外,本文把配置参数、典型场景与运维要点整理成一套可以直接落地的做法。

一、先理解三个核心指令

限流的全部逻辑建立在三条指令上:

nginx
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

server {
    location /api/ {
        limit_req zone=mylimit burst=20 nodelay;
        limit_req_status 429;
    }
}
  • limit_req_zone 定义一块共享内存区。$binary_remote_addr 表示按客户端 IP 的二进制形式作为限流 key,比字符串形式更省内存;zone=mylimit:10m 分配 10MB 共享内存存放状态;rate=10r/s 是允许的长期平均速率。
  • limit_req 在具体 location 上引用这块区域。burst=20 表示允许 20 个请求的突发额度;nodelay 让突发额度内的请求立即处理,不排队、不人为延迟。
  • limit_req_status 429 把超限响应码定为 429。默认的 503 容易与后端故障混淆,429 的语义就是"请求过多",便于监控与客户端重试逻辑区分处理。

一条请求进来后的完整判定流程如下图:速率内直接放行;超速时先看 burst 是否还有余量,有余量且配置了 nodelay 就立即处理;配额用尽则返回 429,并可通过 error_page 转到自定义响应。

Nginx limit_req 请求判定流程

二、场景一:抢购详情页,宽速率加大突发

春节大促期间,单个商品页面的峰值 QPS 可能上万。这类场景不怕流量大,就怕流量把后端打垮,策略是给足均值速率和突发额度:

nginx
location /product/ {
    limit_req_zone $binary_remote_addr zone=product:10m rate=200r/s;
    limit_req zone=product burst=1000 nodelay;
    proxy_pass http://product_backend;
}

每秒 200 次的均值配上 1000 的突发额度,秒杀瞬间的点击洪峰被 Nginx 吸收削平,到达后端的是相对平滑的流量。

三、场景二:验证码接口,严格限流防刷

验证码接口一旦被脚本刷量,短信或邮件成本会直接失控。这类接口要卡得非常紧:每秒 1 次、最多突发 3 次,超限直接拒绝并返回结构化提示。

nginx
location /api/captcha/ {
    limit_req_zone $binary_remote_addr zone=captcha:10m rate=1r/s;
    limit_req zone=captcha burst=3 nodelay;
    limit_req_status 429;
    error_page 429 = @too_many_requests;
}

location @too_many_requests {
    default_type application/json;
    return 429 '{"code": 429, "message": "Too Many Requests", "error": "请求过于频繁,请稍后再试"}';
}

把错误响应约定成 JSON,前端拿到 code 就能弹出友好提示,引导用户调整操作频率,而不是面对一个裸的 503 页面。

四、场景三:按用户等级差异化限流

不同等级的用户本就该有不同的配额。借助 map 把请求头里的用户等级映射成不同速率,同一个 location 就能实现分级限速:

nginx
map $http_x_user_level $api_limit_rate {
    default        "10r/s";
    "vip"          "50r/s";
    "svip"         "100r/s";
}

limit_req_zone $binary_remote_addr zone=api:20m rate=$api_limit_rate;

普通用户 10r/s,vip 50r/s,svip 100r/s,付费用户的体验得到保障,免费额度也不被滥用。

五、三个进阶玩法

多维度限流。 IP 与用户 ID 各建一个 zone,在同一个 location 里同时生效,防止单人换 IP 绕过限制:

nginx
limit_req_zone $binary_remote_addr zone=ip:10m rate=10r/s;
limit_req_zone $uid zone=user:10m rate=5r/s;

location /api/ {
    limit_req zone=ip burst=10 nodelay;
    limit_req zone=user burst=5 nodelay;
}

白名单放行。 用 geo 配合 map,让内网网段和监控探针不受限流影响:

nginx
geo $limit {
    default 1;
    10.0.0.0/8 0;
    192.168.0.0/16 0;
}

map $limit $limit_key {
    0 "";
    1 $binary_remote_addr;
}

limit_req_zone $limit_key zone=mylimit:10m rate=10r/s;

命中白名单时 key 为空字符串,Nginx 对空 key 的请求不计入限流,探针和内部调用得以畅通。

动态调整速率。 固定参数应付不了大促临时扩容、故障降级这类变化,这时可以走动态化路线:在 OpenResty 环境里用 Lua 脚本从 Redis 读取当前的限流配置,请求阶段按最新值执行拦截,运维改一个 Redis 键就能全集群生效。不同版本的扩展模块接口差异较大,落地前需按所用环境验证。

六、监控与日志

限流上线后必须能看见它的工作状态:

nginx
location /nginx_status {
    stub_status on;
    access_log off;
}

map $limit_req_status $loggable {
    429 1;
    default 0;
}

access_log /var/log/nginx/limit.log combined if=$loggable;

stub_status 暴露连接数等基础指标;下面的写法只在请求被限流时记一条日志,日志文件里全是 429,统计被限流量、定位被刷接口都非常直接。

七、最佳实践清单

Nginx 三类场景差异化限流参数与最佳实践

  1. 容量规划。 zone 的 1MB 内存大约能存 16000 个 IP 的状态,估算峰值独立 IP 数后按 2 倍余量设置,避免内存不足导致限流失效。
  2. 分级限流。 全局限流兜底,接口级限流针对热点路径,用户级限流实现差异化,三层叠加才能既保住系统又不误伤正常业务。
  3. 监控告警。 对 429 状态码单独建监控,配合限流日志分析设置告警阈值,流量异常时第一时间知道是哪个接口在承压。
  4. 定期调优。 限流参数没有万能值,周期性分析访问日志,用压测验证新配置,必要时做 A/B 对比,让参数跟随业务演进。

结语

请求限流是花小钱防大事故的典型手段:几行配置,换来的是突发流量下的服务稳定性。建议每个上线前的接口都过一遍限流设计,先在压测环境找到适合自己业务量级的参数组合,再带着监控和日志上线,让每一次拦截都看得见、可解释。

相关文章

分享: