百万级在线用户下,limit_conn高效运行需精准控制计数粒度、压缩键空间、分层限流及系统协同;用$binary_remote_addr、合理设zone大小、缩短keepalive_timeout、双层限流、关闭冗余日志并加强监控。

要让 limit_conn 在百万级在线用户场景下稳定高效运行,关键不是盲目扩大内存区,而是精准控制计数粒度、压缩键空间、规避内存爆炸,并与系统资源协同。单靠堆内存解决不了问题,必须从设计逻辑入手。
合理定义 limit_conn_zone 的键和大小
内存区(zone)本质是一张哈希表,容量取决于键的数量和每个键的内存开销。百万用户不等于需要存百万个键——重点在于“活跃并发连接”的分布特征:
- 用
$binary_remote_addr替代$remote_addr:IPv4 地址仅占 4 字节,加上结构体开销约 64 字节/客户端;若用字符串形式的$remote_addr,平均要 15–20 字节+对齐填充,内存占用翻倍 - 避免使用高基数键:如
$uri、$args或$http_user_agent,单个 zone 可能瞬间膨胀至 GB 级,直接 OOM - 按需选择键维度:纯 IP 限流(
$binary_remote_addr)适合防爬;若需租户隔离,可用$server_name(域名粒度低,通常几百个);多租户+IP 组合可考虑$server_name$binary_remote_addr,但需预估最大组合数 - 计算示例:10MB zone 存 IPv4 客户端 ≈ 10×1024×1024 ÷ 64 ≈ 16 万个独立 IP;支撑百万并发,意味着平均每个 IP 要维持 6–7 个连接,此时 zone 大小建议 ≥32MB(容纳约 32 万 IP 状态)
分层限流 + 连接生命周期管控
百万在线 ≠ 百万活跃连接。大量空闲 keepalive 连接会长期占满配额,导致真实请求被误限:
- 显式缩短空闲连接寿命:
keepalive_timeout 15s;(而非默认 60–75s),配合客户端合理设置Connection: keep-alive和max-age - 启用双层防护:
limit_conn perip 20;+limit_conn global_total 100000;,前者防单 IP 滥用,后者保全局连接池不被耗尽;global zone 键可设为常量(如"global"),仅占 1 个槽位 - HTTP/2 场景注意:一个 TLS 连接承载多个 stream,仍只计为 1 个连接,因此
limit_conn对 HTTP/2 天然友好,无需额外适配
配套调优降低内存与日志压力
zone 本身只是计数器,但周边配置不当会间接放大内存或 CPU 消耗:
- 关闭冗余日志:
limit_conn_log_level error;(默认 warn),防止限流触发时刷爆磁盘 I/O - 禁用非必要状态记录:不在
log_format中滥用$limit_key或$limit,除非确需审计;否则每个日志行多一次字符串拼接+内存拷贝 - 搭配降速策略:
limit_rate 128k;对已超限但仍存活的连接限速,避免其持续占用连接槽位下载大文件 - 确保系统级支持:
worker_rlimit_nofile和ulimit -n必须 ≥ zone 预期最大连接数 × 1.2,否则连接建立失败前就卡在系统层
验证与可观测性要点
上线后不能只看是否报错,要确认 zone 实际负载是否健康:
- 用
nginx -T | grep limit_conn_zone核对 zone 定义位置(必须在 http 块)及语法 - 通过
stub_status或ngx_http_api_module(Nginx Plus)查看limit_conn当前命中数、拒绝数 - 监控
Shared memory zone "perip" (10485760 bytes) used类似日志(需开启error_log ... debug;临时抓取),确认内存使用率长期 ≤70% - 压测时观察
ss -s输出的tcp连接数与 NginxActive connections是否匹配,排除内核队列堆积


















