Nginx可通过map+limit_req_zone组合实现IP与业务标识(如X-Partner-ID)的复合维度限流:先用map构造动态键(如"$remote_addr-$http_x_partner_id"),再为不同partner定义独立zone,最后在location中按条件变量调用对应zone。

Nginx 的 limit_req 模块本身不直接支持“按 IP + 业务标识”复合维度限流,但可以通过合理组合 map、limit_req_zone 和请求头(如 X-Partner-ID)或域名、路径等上下文,实现面向不同合作伙伴的差异化 QPS 控制。
用 map 构建动态限流键
核心思路是把“合作伙伴标识”和“客户端 IP”拼接成唯一限流键。Nginx 不支持直接在 limit_req_zone 中写表达式,所以先用 map 提取并组合:
(放在 http 块中)
map $http_x_partner_id $partner_key {
default "$remote_addr";
"~^(abc|def|xyz)$" "$remote_addr-$http_x_partner_id";
}
这样,带合法 X-Partner-ID: abc 的请求,限流键变成 192.168.1.100-abc;未提供或非法值的,默认只按 IP 限流(兜底策略)。
定义多档 limit_req_zone
为不同合作伙伴设置独立 zone,各自控制 QPS 和突发容量:
limit_req_zone $partner_key zone=partner_abc:10m rate=10r/s; limit_req_zone $partner_key zone=partner_def:10m rate=5r/s; limit_req_zone $partner_key zone=partner_xyz:10m rate=20r/s;
注意:每个 zone 名称必须唯一,且 key 必须与 map 输出一致。内存大小(如 10m)按预估并发 IP 数 × 每个 key 占用约 64 字节估算。
在 server 或 location 中按需应用
不能全局统一用一个 limit_req,而要结合条件判断,匹配对应 zone:
server {
location /api/ {
# 根据 header 动态选择限流策略
if ($http_x_partner_id = "abc") {
set $use_zone "partner_abc";
}
if ($http_x_partner_id = "def") {
set $use_zone "partner_def";
}
if ($http_x_partner_id = "xyz") {
set $use_zone "partner_xyz";
}
<pre class="brush:php;toolbar:false;"> # 使用变量触发限流(Nginx 1.13.10+ 支持变量 zone 名)
limit_req zone=$use_zone burst=20 nodelay;
proxy_pass http://backend;
}
}
注意:变量 zone 名依赖较新版本 Nginx;若版本较低(如 1.12),需改用多个 location 分支或通过不同子域名/路径隔离。
补充建议与避坑点
- 合作伙伴标识尽量由服务端注入(如 API 网关统一加 header),避免客户端伪造;可在限流前用
auth_request或map配合白名单校验X-Partner-ID - 对测试或内部调用可设宽松 zone(如
rate=100r/s),通过$host或$arg_env区分环境 - 开启
limit_req_log_level warn和limit_req_status 429,配合日志分析被限流的 partner 和 IP 分布 - 避免在高并发场景下滥用
if;更健壮的做法是用 OpenResty 的 Lua 脚本做键生成和 zone 路由











