可通过 limit_req_zone 指令结合 $http_x_partner_id 变量,为不同合作伙伴实现独立 qps 限流;需用 map 处理空/非法 header 值,支持 burst 排队、多 zone 分级限流及 openresty 动态配额。

可以通过 limit_req_zone 指令配合自定义请求头(如 X-Partner-ID)来为不同合作伙伴分配独立的 QPS 限额,实现彼此隔离、互不影响的限流策略。
定义基于自定义 Header 的限流区域
Nginx 支持将任意变量作为 limit_req_zone 的键。只要在请求中携带了明确标识合作伙伴的 Header(例如 X-Partner-ID: abc123),就可以用 $http_x_partner_id 引用它:
- 在
http块中添加限流区域定义:limit_req_zone $http_x_partner_id zone=partner_qps:10m rate=10r/s; - 其中
zone=partner_qps:10m表示共享内存区大小为 10MB,足够存储数万个 partner ID 的计数器;rate=10r/s表示每个 partner 默认最多 10 QPS。 - 注意:若 Header 不存在或为空,
$http_x_partner_id值为空字符串,所有无该 Header 的请求会归入同一个“空 key”桶——建议配合if或map做兜底处理。
在 location 中启用并配置限流行为
在具体服务的 location 块中调用该区域,并控制突发请求和排队逻辑:
- 基础限流:
limit_req zone=partner_qps burst=20 nodelay;
表示允许每个 partner 最多突发 20 个请求,超出立即拒绝(503)。 - 更友好的方式是允许排队等待:
limit_req zone=partner_qps burst=20;
Nginx 会将超出速率的请求在队列中等待,超时(默认 1 秒)则丢弃。 - 可叠加全局限流作兜底,例如限制单 IP 总量:
limit_req zone=ip_all burst=5;(需另定义limit_req_zone $remote_addr zone=ip_all:10m rate=100r/s;)
增强健壮性:校验与兜底
仅依赖 Header 不够安全,需防止伪造或缺失:
- 用
map将非法/空值映射为统一 fallback key,避免污染主限流区:map $http_x_partner_id $partner_key {<br> default $http_x_partner_id;<br> "" "invalid";<br> "~^[a-zA-Z0-9_-]{3,32}$" $http_x_partner_id;<br> else "invalid";<br>}
再基于$partner_key定义限流区,确保非法值走统一限流通道。 - 结合
auth_request或内部鉴权接口,在限流前验证该 partner ID 是否合法、是否启用、QPS 配额是否动态变更(此时需配合 OpenResty 或外部限流服务)。 - 记录实际生效的 partner ID 和拒绝原因,便于排查:
log_format limitlog '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_partner_id" $limit_req_status';$limit_req_status为passed/rejected/delayed。
支持差异化配额:按 partner 动态设置 rate
原生 Nginx 不支持 per-key 动态 rate,但可通过以下方式变通实现:
- 为高频 partner 单独建 zone:
limit_req_zone $http_x_partner_id zone=partner_premium:10m rate=100r/s;<br>limit_req_zone $http_x_partner_id zone=partner_basic:10m rate=10r/s;
再用map判断 partner 类型,选择对应 zone(需配合limit_req的zone=...多次声明,Nginx 会按顺序匹配首个生效的)。 - 使用 OpenResty + Lua,读取 Redis 中各 partner 的实时配额,调用
resty.limit.count实现完全动态控制,适合配额频繁调整的场景。 - 通过上游服务返回响应头(如
X-RateLimit-Partner: 50),再用map+split_clients或 Lua 解析并映射到不同 zone —— 更复杂但灵活性高。











