limit_req_zone仅限请求速率而非并发连接数;百万级反代需分层协同:limit_req_zone控速率、limit_conn_zone管连接、upstream长连接减开销、系统调优保资源,核心是避免四层资源混用。

直接说结论:limit_req_zone 本身不处理“并发连接数”,它限制的是单位时间内的请求数(速率),不是同时在线连接数;百万级“反代并发”应拆解为“百万级请求速率”和“高并发连接管理”两个维度,前者靠 limit_req_zone 合理配置,后者靠 limit_conn_zone + upstream 长连接 + 系统调优协同实现。
真正卡住百万级反代能力的,从来不是 limit_req_zone 的内存算错,而是把限流、连接、代理、系统四层资源混为一谈。
limit_req_zone 内存只管“活跃 IP 数”,不是“QPS 数”
每个被限速的客户端 IP(用 $binary_remote_addr)在共享内存中占约 64 字节,与你每秒发多少请求无关,只与同一时刻有多少不同 IP 正在触发限速逻辑有关。
- 1 MB ≈ 存储 1.6 万个活跃 IP
- 10 MB ≈ 支持约 16 万个活跃 IP
- 100 万个活跃 IP → 至少需 64 MB 共享内存(100w × 64B)
但注意:生产环境极少有 100 万 不同真实客户端 IP 在 1 秒内同时触发限速(比如访问 /api/login)。更常见的是:
- CDN 回源 IP 集中(可能就几十个)
- 移动端 App 使用固定出口网关(几百个 NAT IP)
- 爬虫集群伪装成不同 IP,但真正高频活跃的往往只有几千个
✅ 建议做法:
- 用
awk '{print $1}' access.log | tail -n 300000 | sort -u | wc -l统计最近 5 分钟去重 IP 数 - 再乘以 0.4~0.7(剔除低频探针、健康检查等)得「有效活跃 IP 数」
- 乘以 64,向上取整到最接近的 MB 单位,并加 20% 冗余
例如:统计得 8 万个活跃 IP → 80000 × 64 = 5.12 MB → 配 zone=perip:10m
百万级反代压力,真正吃内存和 CPU 的是 upstream 连接管理
limit_req_zone 只做请求准入控制,不接管后端连接。当 Nginx 作为反代,百万 QPS 意味着:
- 每秒新建数千 upstream 连接(若无长连接)
- 每个连接占用 socket、buffer、TLS 上下文等资源
- worker 进程频繁调度、锁竞争加剧
✅ 必须配套的关键配置:
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
keepalive 200; # 每个 worker 保持的空闲长连接数
keepalive_requests 1000; # 单连接复用次数上限
keepalive_timeout 60s;
}
server {
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection ''; # 清除 Connection 头,启用长连接
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 关键缓冲区调大,防小包堆积
proxy_buffer_size 128k;
proxy_buffers 8 256k;
proxy_busy_buffers_size 512k;
}
}
同时系统层要放开限制:
worker_rlimit_nofile 1048576;-
/etc/security/limits.conf中设* soft nofile 1048576 sysctl -w fs.file-max=2097152
百万级场景必须放弃“单 zone 全量记 IP”的幻想
硬撑百万独立 IP 的 limit_req_zone(需 ≥64MB)不仅浪费,还带来哈希冲突升高、LRU 淘汰频繁、reload 时内存重建慢等问题。
✅ 更务实的分层策略:
第一层(全局兜底):
zone=global:2m rate=1000r/s,key 用$server_name,防整个域名被扫垮第二层(路径分级):
zone=login:10m rate=5r/m,key 用$binary_remote_addr$http_user_agent,专防爆破-
第三层(白名单跳过):
map $remote_addr $limit_key { default $binary_remote_addr; ~^192\.168\. ""; ~^10\. ""; 127.0.0.1 ""; } limit_req_zone $limit_key zone=api:20m rate=10r/s;内网、监控、CDN 回源 IP 直接不进限流区,省下全部 64 字节
-
第四层(聚合降维):对海外流量按国家限流
geo $country_code { default ZZ; include /etc/nginx/geoip.conf; } limit_req_zone $country_code zone=bycountry:5m rate=100r/s;
验证是否真撑得住百万级反代
别只看 nginx -t 和日志有没有 503,重点查三处:
内存是否够用:
grep "limiting requests" /var/log/nginx/error.log | tail -20
若频繁出现且$limit_status日志里大量显示"hit"后又快速消失 → 很可能是 zone 太小,状态被挤掉连接是否复用成功:开启
ngx_http_upstream_module状态页,或用ss -s | grep "tcp:"看 ESTAB 连接数是否稳定在预期长连接池范围内上游响应是否拖慢:用
proxy_next_upstream error timeout http_500+proxy_next_upstream_tries 3避免单点抖动放大
实测经验:一台 16 核 32G 的 Nginx 反代节点,在合理配置下,稳定支撑 80w QPS(后端平均响应 20ms)、并发连接 12w+,其中
limit_req_zone总共只用了 15m 内存,核心在于“不记无效 IP、不压单 zone、不裸连 upstream”。
不复杂但容易忽略。










