在 nginx 中,limit_conn_zone 必须在 http 块内定义,通过 key 参数指定用于识别客户端的变量(如 ip 或会话 id),并使用 zone 参数分配共享内存的名称、大小及存储类型。该指令的作用域仅限于 http 层,不能在 server 或 location 块中直接创建新的内存区域,但可以在子块中引用已定义的内存区域来实施连接限制。

如何指定 key 和 zone 名称
定义共享内存时,需要明确两个核心要素:一是作为键的变量,二是内存区域的命名与容量。通常使用 $binary_remote_addr 作为键,因为它比 $remote_addr 更节省内存空间(固定 4 字节)。内存区域名称是自定义的字符串,后面紧跟一个冒号和大小值,例如 my_zone:10m 表示创建一个名为 my_zone 的区域,大小为 10MB。每个 1MB 的内存大约可以存储 3.2 万个 64 位键值对。

http {
# 定义共享内存区域
# key: $binary_remote_addr (客户端IP)
# zone: my_limit_conn_zone (区域名) : 10m (大小)
limit_conn_zone $binary_remote_addr zone=my_limit_conn_zone:10m;
server {
listen 80;
server_name example.com;
location / {
# 引用上面定义的内存区域
limit_conn my_limit_conn_zone 10;
proxy_pass http://backend;
}
}
}
上述配置中,limit_conn_zone 位于 http 块顶层,确保了所有虚拟主机都能访问该内存池。如果需要在不同的 server 块中使用不同的限制策略,可以定义多个 limit_conn_zone 指令,分别指向不同的内存区域,例如同时定义基于 IP 和基于会话的内存区。
内存区域的大小计算与冲突处理
合理设置内存大小至关重要,过小会导致频繁驱逐旧数据,影响限流准确性;过大则浪费服务器内存。Nginx 使用哈希表存储键值对,每个桶(bucket)存储特定前缀的键。当多个 limit_conn_zone 指令使用相同的 zone 名称时,会发生覆盖错误,导致配置加载失败。因此,每个内存区域必须有唯一的名称。

# 错误示例:重复的 zone 名称 limit_conn_zone $binary_remote_addr zone=my_zone:10m; limit_conn_zone $session_id zone=my_zone:10m; # 报错:duplicate zone name
正确的做法是为不同用途分配不同名称。例如,若需同时限制 IP 并发数和用户会话数,应定义两个独立区域:limit_conn_zone $binary_remote_addr zone=ip_zone:10m; 和 limit_conn_zone $cookie_session_id zone=session_zone:10m;。这样,limit_conn 指令在 location 中可分别引用 ip_zone 或 session_zone,互不干扰。内存大小的估算公式为:总键数 = (内存大小 MB * 1024 * 1024) / (键长度 + 额外开销)。对于 $binary_remote_addr,键长为 4 字节,额外开销约 16-20 字节,故 1MB 约存 3.2 万条记录。
基于同一内存区的多条件限制结果
当在同一个 location 块中引用同一个 limit_conn_zone 并设置多个 limit_conn 指令时,Nginx 会对所有条件取交集。例如,若同时限制单个 IP 最大连接数为 10,且全局最大连接数为 100,则只有当客户端 IP 连接数 ≤10 且总连接数 ≤100 时才允许通过。任一条件超限均返回 503 错误。这种机制允许细粒度控制,但需注意内存区内的计数是累加的,即每个 limit_conn 指令都会检查同一内存区中的对应键值。











