nginx的limit_req模块基于漏桶算法实现平滑限流,需在http块定义limit_req_zone(如$uri zone=api_order:10m rate=5r/s),并在location = /api/v1/order中用limit_req zone=api_order burst=10 nodelay启用,配合limit_req_status 429和$log限流日志提升可观测性。

要通过 Nginx 的 limit_req 模块在代理层对特定接口实现“漏桶”式平滑限流,核心是正确组合 limit_req_zone 定义与 limit_req 应用,并注意其底层行为本质——limit_req 实际模拟的是**漏桶(Leaky Bucket)**,而非令牌桶(Token Bucket),即请求以固定速率“漏出”,超量请求会被延迟或拒绝。
定义基于接口路径的限流区域
漏桶限流需按需区分维度。若只针对某接口(如 /api/v1/order),推荐用 $request_uri 或自定义变量提取路径,避免误伤其他接口:
limit_req_zone $request_uri zone=api_order:10m rate=5r/s;
说明:
-
$request_uri包含完整路径+查询参数,适合精确匹配单个接口;若需忽略参数(如/api/v1/order?id=123和/api/v1/order?id=456视为同一接口),改用$uri(不含 query string) -
zone=api_order:10m表示该共享内存区名为api_order,大小 10MB,足够存储数万级 IP+URI 组合状态 -
rate=5r/s即每秒最多允许 5 个请求“漏出”,超出部分将被延迟(默认行为)或拒绝(需显式配置burst和nodelay)
在 location 块中精准启用限流
仅对目标接口生效,不要放在 server 级别全局应用:
location = /api/v1/order {
limit_req zone=api_order burst=10 nodelay;
proxy_pass http://backend;
proxy_set_header Host $host;
}关键点:
- 使用
location =进行精确匹配,避免前缀匹配(如location /api)误伤其他子路径 -
burst=10设置缓冲队列容量为 10 个请求,用于吸收瞬时流量高峰 -
nodelay表示不延迟排队请求,超rate但未超burst的请求立即处理;若去掉nodelay,则按漏桶节奏匀速放行(更平滑,但可能增加延迟) - 若希望严格平滑、不允许任何突发,可写成
burst=0或直接省略burst(此时burst默认为 0)
控制响应行为与日志可观测性
限流触发后,默认返回 503,可通过以下方式优化体验和排查:
- 自定义错误页:
limit_req_status 429;将限流响应码改为标准的 429 Too Many Requests - 记录被限流请求:
log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$limit";其中$limit变量会显示当前匹配的限流 zone 名 - 临时调试时可加
limit_req_log_level warn;,让被限流请求在 error log 中以 warn 级别输出,便于确认是否命中规则
验证与常见避坑提示
部署后建议用简单脚本压测验证效果:
for i in {1..20}; do curl -s -o /dev/null -w "%{http_code}\n" http://your-domain.com/api/v1/order & done; wait预期:在 1 秒内发出 20 次请求,rate=5r/s + burst=10 下,前 15 个应成功(5 个实时 + 10 个进 burst 缓冲),后续 5 个大概率返回 429 或 503(取决于是否设 nodelay)。
避坑提醒:
- 确保
limit_req_zone声明在http块内,不能放在server或location内 -
burst不是并发数,而是“等待队列长度”;实际吞吐仍受rate约束,burst 只影响瞬时接纳能力 - 若接口需鉴权且限流应基于用户 ID(非 IP 或 URI),需先在
access_by_lua_block或auth_request中提取$user_id,再用于limit_req_zone










