Nginx不仅是最流行的Web服务器和反向代理,在四层(传输层)代理场景中也扮演着重要角色。当Nginx被用作TCP/UDP负载均衡器时,如果放任客户端无限制地建立连接,后端服务很容易被突发的连接洪流压垮。ngx_stream_limit_conn_module就是为解决这一问题而生的官方模块,它可以在stream上下文中按照指定键值统计并发连接数,超过阈值后直接拒绝新连接。本文将从模块原理、配置指令、实战示例和常见问题几个方面,详细讲解这个模块的使用方法。

一、模块原理与编译前提
ngx_stream_limit_conn_module的定位是四层版本的连接限制器,与HTTP层的limit_conn模块思路一致,但作用对象是TCP或UDP会话而非HTTP请求。它的工作原理是在共享内存中维护一张连接计数表,每当有新连接建立时,模块会根据配置的键(通常是客户端IP)在表中查找对应的计数器并加一;当连接关闭时计数器减一。如果某个键的计数器已经达到limit_conn设定的上限,新连接会被立刻关闭,不会转发到后端服务器。
需要特别注意的是,这个模块从Nginx 1.9.x版本开始提供,并且默认情况下不会被编译进Nginx,需要在使用configure脚本构建时显式加上--with-stream_limit_conn_module参数。不过在大多数主流发行版的官方仓库或常用第三方仓库中,带stream功能的Nginx包通常已经包含该模块,可以通过nginx -V命令查看编译参数来确认。如果编译参数中没有该模块,就需要自行编译安装。
该模块的配置指令必须放在stream块中,不能放在http块里,这是初学者最常犯的错误之一。如果你的配置文件里没有stream块,需要先确保Nginx支持stream功能(编译时开启--with-stream),再进行相关配置。
二、核心指令详解:limit_conn_zone与limit_conn
limit_conn_zone用于定义连接计数所需的共享内存区和计键方式,它接受两个参数:第一个是键表达式,第二个是大小参数。键表达式最常用的写法是$remote_addr,表示按客户端IP地址统计连接数。如果想按网段统计,可以配合geo模块生成的变量使用;如果客户端经过了前置代理,也可以使用$proxy_protocol_addr获取真实来源地址。
# 在stream块中定义共享内存区,命名为addr,分配10MB内存
# 按 $remote_addr(客户端IP)作为计数键
stream {
limit_conn_zone $remote_addr zone=addr:10m;
server {
listen 3306;
proxy_pass mysql_backend;
# 限制每个客户端IP最多同时建立20个连接
limit_conn addr 20;
}
}limit_conn指令则是真正生效的限制动作,第一个参数引用limit_conn_zone定义的zone名称,第二个参数是允许的最大并发连接数。当某个键的连接数达到这个数值后,后续的新连接会被Nginx直接关闭,客户端通常表现为连接被重置或立即断开。共享内存区的大小决定了能跟踪多少个不同的键,10MB大约可以记录几十万个IP(不含IPv6的情况下每个条目占用约32到64字节),对绝大多数场景绰绰有余。如果内存区耗尽,Nginx会在错误日志中记录相应信息,并对后续请求直接拒绝。
除了按IP限制,还可以按整个虚拟服务器限制总连接数,写法是将键固定为一个常量值:
stream {
# 定义一个固定键,用于统计整个server的连接总数
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
listen 5432;
proxy_pass pg_backend;
limit_conn perip 10;
}
server {
listen 6379;
proxy_pass redis_backend;
# 键恒为1,相当于限制该server总连接数不超过5000
limit_conn_zone 1 zone=simulated:10m;
limit_conn simulated 5000;
}
}三、辅助指令与调试技巧
模块还提供了几个辅助指令,在实际运维中非常实用。limit_conn_log_level用于设置连接被拒绝时日志的级别,默认是error,可以调整为notice或info,方便在排查时快速定位被限流的连接:
stream {
limit_conn_zone $remote_addr zone=addr:10m;
server {
listen 3306;
proxy_pass mysql_backend;
limit_conn addr 20;
# 连接超限时以notice级别记录日志
limit_conn_log_level notice;
}
}limit_conn_dry_run是另一个非常有用的指令,开启后模块进入演练模式:所有超限的连接不会被真正拒绝,只会在日志中记录本来会被拒绝的信息。这个功能非常适合在上线限流策略之前观察影响范围,避免误伤正常用户。可以先开启dry_run运行几天,分析日志确认阈值合理后再切换到真实生效模式。
此外,模块还内置了$limit_conn_status变量,值为rejected或passed,可以在日志格式中引用它来精确统计被拒绝的连接数量,为容量规划提供数据支撑。建议在stream的log_format中显式输出这个变量,配合日志分析工具就能得到限流命中率的趋势图。
四、与HTTP层限流的区别及常见问题
不少刚接触Nginx四层代理的人容易混淆ngx_stream_limit_conn_module和ngx_http_limit_conn_module。两者的指令名称几乎相同,但有本质区别:HTTP层的模块工作在七层,统计的是并发HTTP请求数,键通常用$binary_remote_addr或$server_name;而stream层的模块工作在四层,统计的是TCP会话或UDP会话数,面向的是数据库、消息队列、游戏服务器等长连接场景。两者不能混用,把limit_conn指令写在http块或把stream的指令写在http块都会导致配置报错。
常见的坑还有以下几点:第一,UDP场景下的连接计数以会话超时为准,Nginx会按照proxy_timeout来判定UDP会话结束,如果超时设置过长,计数器释放会明显滞后,需要合理权衡;第二,如果Nginx前面还有LVS或四层负载均衡器,$remote_addr拿到的是负载均衡器的IP,所有客户端会被算成同一个键,这时必须改用PROXY Protocol并使用$proxy_protocol_addr作为键;第三,limit_conn的数值不是越大越好,要结合后端服务的承载能力和单个连接的资源消耗来估算,配合dry_run模式逐步收敛。
总的来说,ngx_stream_limit_conn_module配置简单、开销极低,是四层代理场景下保护后端服务的第一道防线。掌握好zone定义、键的选择和阈值调优这三个要点,再结合日志观察持续优化,就能让Nginx在TCP/UDP代理场景中既保持高吞吐又具备良好的自我保护能力。
Nginxngx_stream_limit_conn_module流连接限制修改时间:2026-09-02 06:34:33