千万IP直接限流不可行,需按业务维度聚合key、分级配置zone、空key跳过低价值流量,并结合日志与后端兜底形成闭环。

直接靠 limit_req_zone 记住千万个独立 IP 并长期限流,在常规 Nginx 部署中不可行——每个 IP 状态槽实占约 64 字节,千万 IP 就要 640MB 共享内存,且状态无法压缩、淘汰策略被动、超出即失效。所谓“静态高可用限流”,关键不是存全量,而是用确定性策略把内存花在刀刃上,让限流规则稳定生效、不因内存溢出而抖动或跳过。
按业务维度聚合 key,大幅压缩有效状态数
避免用 $binary_remote_addr 直接做 key。换成更高抽象层级的标识,单个 key 覆盖成百上千真实请求源:
- 对海外流量,用
$geoip2_country_code或$geoip_city_continent_code,全球国家/大洲级粒度,10MB 内存可覆盖全部活跃区域 - 对国内用户,用
map指令提取$http_x_forwarded_for前两段(如112.65),代表运营商骨干网出口,比单 IP 更稳定、更少波动 - 对登录态用户,优先用
$http_x_user_id或$cookie_uid;未登录时再 fallback 到 IP 前缀,确保核心行为始终绑定身份而非临时地址
分级 zone 配置,让内存各司其职
不设一个“全能 zone”,而是按风险等级、路径敏感度、存活周期拆分多个小 zone,总内存可控,但覆盖能力更强:
-
zone=api_burst:5m rate=200r/s:用于 /api/* 下突发流量,key 用$server_name,防整个服务被刷垮,自动淘汰闲置连接 -
zone=login_strict:2m rate=2r/m:仅作用于/login,key 为$binary_remote_addr$http_user_agent组合,防 UA 轮换爆破,长期存活 IP 极少 -
zone=user_action:8m rate=15r/s:key 是$http_x_user_id,真实用户数远低于 IP 总量,限的是人不是设备
白名单与空 key 映射,彻底跳过低价值流量
不是所有请求都值得记状态。用 map 把确定可信的来源映射为空 key,Nginx 不为其分配任何 slot:
- CN2/骨干网回源 IP 段、CDN 节点地址、K8s Service CIDR、健康检查探针(如
/healthz)全部归入空 key - 内部调用头(如
$http_x_internal_call = "true")也映射为空,避免服务间调用挤占用户限流资源 - 配置示例:
map $remote_addr $limit_key { ~^10\. ""; ~^172\.1[6-9] ""; default $binary_remote_addr; }
配合日志与外部兜底,形成闭环判断
Nginx 限流本身不提供分析能力,需靠日志反馈驱动策略迭代:
- 开启
limit_req_log_level warn,并在 access log 中记录$limit变量,识别“未被限却高频”的 IP 段 - 对日志中持续出现的异常网段,动态更新
map白名单或新增粗粒度 zone(如针对某 IDC 单独限流) - 千万级场景下,Nginx 应作为第一道轻量防线,后端服务仍需自带熔断+滑动窗口限流,避免单点失效










