核心是用业务特征(如用户ID、API路径前缀、设备类型等)替代IP作为限流键,实现精细化限流;通过自定义Nginx变量构造limit_req_zone,并在location中按需启用,配合burst/nodelay策略与日志监控验证效果。

核心思路是:不只用 IP,而是把业务特征(比如用户 ID、API 路径前缀、请求头标识、设备类型等)作为限流键,让限流策略贴合真实业务场景。
选择并构造有意义的限流键变量
limit_req_zone 的第一个参数必须是 Nginx 变量,它决定了“谁”被统一计数。默认用 $binary_remote_addr 是按客户端 IP 限流,但你可以替换成任意能反映业务维度的变量:
-
按用户身份限流:提取请求头中的 token 或 cookie,例如
$http_x_user_id(需上游透传)或$cookie_uid; -
按 API 分组限流:用
$request_uri截取前缀,如set $api_group $1;配合if ($request_uri ~ ^/v1/(orders|payments)/) { set $api_group "payment_api"; }; -
按客户端类型区分:检查
$http_user_agent,匹配 mobile/web/app,再赋值给自定义变量如$client_type; -
按租户或子域名限流:使用
$host或$server_name,或从$http_x_tenant_id提取。
在 http 块中定义带业务语义的 zone
变量确定后,直接用于 limit_req_zone 声明。注意内存大小要预估好——比如你有 10 万个活跃用户,每个状态约 32 字节,10m 内存可支撑约 32 万 key,够用:
http {
# 按用户 ID 限流:每秒最多 5 次操作
limit_req_zone $cookie_uid zone=user_rate:10m rate=5r/s;
<pre class="brush:php;toolbar:false;"># 按 API 分组限流:支付类接口每秒最多 20 次
limit_req_zone $api_group zone=api_group:5m rate=20r/s;
# 按租户限流:每个租户每分钟最多 300 次调用
limit_req_zone $http_x_tenant_id zone=tenant_rate:10m rate=300r/m;}
在 location 中精准启用对应限流策略
不同业务路径挂不同的 zone,互不影响。burst 和 nodelay 的取舍取决于容忍度:
- 对登录、下单等关键操作,建议
burst=3 nodelay:允许极小突发,超了立刻拒,防暴力试探; - 对数据看板、列表查询等读多写少接口,可用
burst=20不加 nodelay:排队缓冲,提升用户体验; - 对第三方回调接口,可结合 if 判断来源,只对特定 header 启用限流,避免误伤。
示例:
location /api/v1/orders/ {
limit_req zone=user_rate burst=3 nodelay;
proxy_pass https://www.php.cn/link/65b5b8d1f89bf53a5713bc3afdd83e9e;
}
<p>location /api/v1/dashboard/ {
limit_req zone=user_rate burst=20;
proxy_pass <a href="https://www.php.cn/link/65b5b8d1f89bf53a5713bc3afdd83e9e">https://www.php.cn/link/65b5b8d1f89bf53a5713bc3afdd83e9e</a>;
}配合日志与监控验证效果
光配对不行,得看得见。加上日志级别和自定义响应头,方便排查:
- 加
limit_req_log_level warn;到对应 location,被限流的请求会记 warn 日志; - 用
add_header X-RateLimit-Remaining $limit_rate_remaining;(需配合 lua 或 openresty 扩展)返回余量; - 在 access_log 中记录限流状态,比如用
$limit_rate_status变量(Nginx Plus 支持,开源版需 patch 或用 lua-resty-limit-traffic)。
这样就能确认:是不是真按用户 ID 限了?哪个租户打爆了配额?突发流量是否被合理吸收?

















