一、Nginx Stream模块是什么
Nginx 不仅可以处理 HTTP/HTTPS 请求,还可以借助 Stream 模块代理 TCP、UDP 等四层网络流量。当后端服务不是标准 HTTP 服务,而是 Socket、数据库、消息队列、游戏服务或其他 TCP 服务时,Stream 就非常适合用于端口转发。
典型场景如下:
客户端 │ │ TCP连接 ▼ Nginx:9000 │ │ Stream转发 ▼ 后端服务器:8080
客户端只需要连接 Nginx 暴露的端口,Nginx 再将 TCP 连接转发到指定的后端地址。
与 http {} 中的 proxy_pass 不同,Stream 工作在传输层,不需要理解 HTTP 请求内容,因此不会修改 HTTP Header,也不会依赖 URL、Host 等 HTTP 信息。
二、检查Nginx是否支持Stream模块
配置 Stream 之前,首先需要确认当前 Nginx 是否编译了 Stream 模块。
执行:
Bashnginx -V
查看输出中的编译参数,例如:
--with-stream
如果能够看到:
--with-stream=dynamic
则表示 Stream 以动态模块方式提供。
部分发行版可能需要单独安装对应模块。以 Debian/Ubuntu 系统为例,可以先检查相关模块包:
Bashapt search nginx-module-stream
CentOS、Rocky Linux、AlmaLinux 等系统则可以通过:
Bashdnf search nginx stream
或者:
Bashyum search nginx stream
进行确认。
如果使用的是源码编译的 Nginx,可以在编译时加入:
Bash./configure --with-stream make make install
安装完成后再次执行:
Bashnginx -V
确认编译参数是否生效。
需要注意的是,stream 并不是放在 http {} 里面使用的。它属于 Nginx 的顶层配置模块。
三、基础Stream TCP转发配置
最简单的 TCP Socket 转发配置如下:
Nginxstream { server { listen 9000; proxy_pass 192.168.1.100:8080; } }
这段配置的含义非常直观:
客户端 → Nginx:9000 → 192.168.1.100:8080
客户端访问:
Bashtelnet nginx服务器IP 9000
或者:
Bashnc nginx服务器IP 9000
建立连接后,Nginx 会把 TCP 数据转发给:
192.168.1.100:8080
如果后端是一个自定义 Socket 服务,这种方式同样适用。
四、将Stream配置放到独立文件中
生产环境中通常不建议把大量 Stream 配置全部写入 nginx.conf,可以通过 include 拆分配置。
主配置:
Nginxworker_processes auto; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; } stream { include /etc/nginx/stream.d/*.conf; }
然后创建:
Bashmkdir -p /etc/nginx/stream.d
例如:
Bashvim /etc/nginx/stream.d/socket.conf
写入:
Nginxserver { listen 9000; proxy_connect_timeout 5s; proxy_timeout 60s; proxy_pass 192.168.1.100:8080; }
这种结构更加方便维护多个 TCP 服务。
例如:
/etc/nginx/ ├── nginx.conf └── stream.d/ ├── mysql.conf ├── socket.conf └── redis.conf
五、Stream模块配置中的关键参数
1. listen
listen 用于定义 Nginx 对外监听的端口。
Nginxlisten 9000;
如果只希望监听指定 IP:
Nginxlisten 192.168.1.10:9000;
IPv6 环境也可以配置:
Nginxlisten [::]:9000;
如果需要 UDP 转发,则需要显式声明:
Nginxlisten 9000 udp;
TCP 和 UDP 的监听方式不能混为一谈。
2. proxy_pass
proxy_pass 指定 Socket 的后端地址:
Nginxproxy_pass 192.168.1.100:8080;
也可以使用域名:
Nginxproxy_pass backend.example.com:8080;
如果后端存在多个服务器,可以配合 upstream:
Nginxstream { upstream socket_backend { server 192.168.1.100:8080; server 192.168.1.101:8080; } server { listen 9000; proxy_pass socket_backend; } }
这样可以实现 TCP 层面的负载均衡。
3. proxy_connect_timeout
控制 Nginx 与后端建立连接时的超时时间:
Nginxproxy_connect_timeout 5s;
如果后端服务器不可访问,超过该时间仍然无法建立连接,Nginx 就会返回连接失败。
4. proxy_timeout
控制代理连接的超时时间:
Nginxproxy_timeout 60s;
Socket 服务如果需要保持长连接,应根据业务特点适当增大这个值。
例如某些长连接服务可能需要:
Nginxproxy_timeout 1h;
不能简单套用 HTTP 服务的短超时配置,否则可能出现 Socket 连接莫名断开的情况。
六、完整的Socket转发配置示例
假设有一个 Socket 服务运行在:
10.10.10.20:7000
希望通过 Nginx 的:
0.0.0.0:9000
对外提供访问,可以配置:
Nginxstream { server { listen 9000; proxy_connect_timeout 10s; proxy_timeout 300s; proxy_pass 10.10.10.20:7000; } }
修改完成后不要直接重启 Nginx,先检查配置:
Bashnginx -t
正常情况下会看到:
syntax is ok test is successful
确认无误后执行:
Bashnginx -s reload
或者使用 systemd:
Bashsystemctl reload nginx
七、Nginx Stream配置不生效的常见原因
1. Stream写进了http配置块
错误写法:
Nginxhttp { stream { server { listen 9000; proxy_pass 192.168.1.100:8080; } } }
这种结构是错误的。
正确方式应该是:
Nginxhttp { ... } stream { server { listen 9000; proxy_pass 192.168.1.100:8080; } }
stream 与 http 应处于同一级配置层级。
2. Nginx没有编译Stream模块
如果执行:
Bashnginx -t
出现类似:
unknown directive "stream"
通常需要检查 Nginx 是否包含 Stream 模块。
执行:
Bashnginx -V 2>&1 | grep stream
如果没有相关信息,就需要安装对应模块或重新编译 Nginx。
3. 动态模块没有加载
如果使用动态 Stream 模块,可能需要在配置文件顶部加载模块,例如具体模块文件路径取决于安装方式:
Nginxload_module modules/ngx_stream_module.so;
然后再配置:
Nginxstream { ... }
如果模块文件不存在,不能直接照搬这个路径,应先通过安装包或系统文件确认实际位置。
八、Socket转发连接失败的排查方法
Socket 转发出现问题时,不要只检查 Nginx 配置。应该按照“监听端口 → Nginx → 网络 → 后端服务”的路径逐层排查。
第一步:确认Nginx监听端口
执行:
Bashss -lntp | grep 9000
或者:
Bashnetstat -lntp | grep 9000
正常情况下应该看到类似:
LISTEN 0 511 0.0.0.0:9000 0.0.0.0:* users:(("nginx",pid=1234,fd=8))
如果完全没有结果,说明 Nginx 并没有监听这个端口。
优先检查:
Bashnginx -t
以及:
Bashnginx -T
nginx -T 特别有用,因为它会输出最终加载的完整配置,可以确认 stream 配置文件是否真的被 include。
第二步:检查后端端口
在 Nginx 服务器上直接连接后端:
Bashnc -vz 10.10.10.20 7000
如果返回:
Connection succeeded
说明 Nginx 到后端的 TCP 网络基本正常。
如果出现:
Connection refused
重点检查后端服务是否启动,以及服务是否监听正确端口。
执行:
Bashss -lntp | grep 7000
如果服务只监听:
127.0.0.1:7000
那么其他服务器无法直接访问。
需要根据实际部署情况调整监听地址,例如:
0.0.0.0:7000
或者后端服务器的实际内网 IP。
第三步:检查防火墙
如果 Nginx 监听正常,但客户端无法连接,需要检查防火墙。
Linux 上可以查看:
Bashfirewall-cmd --list-ports
或者:
Bashiptables -L -n
Ubuntu 使用 UFW 时可以检查:
Bashufw status
例如需要开放 TCP 9000:
Bashfirewall-cmd --permanent --add-port=9000/tcp firewall-cmd --reload
如果服务器部署在云平台,还必须检查云安全组、网络 ACL 等规则。
九、使用telnet和nc定位Socket问题
客户端首先可以执行:
Bashnc -vz NGINX_IP 9000
如果看到:
Connection refused
通常说明目标端口没有监听,或者中间设备主动拒绝连接。
如果出现:
Connection timed out
则更应该检查防火墙、安全组、路由以及网络连通性。
也可以使用:
Bashtelnet NGINX_IP 9000
成功建立连接只能证明 TCP 三次握手完成,并不能证明后端业务协议一定正常。
例如:
客户端 → Nginx
已经连接成功,但:
Nginx → 后端Socket
可能仍然失败。
因此还应该在 Nginx 服务器上单独测试:
Bashnc -vz BACKEND_IP BACKEND_PORT
十、通过tcpdump确认数据到底走到了哪里
遇到“端口能连接,但业务没有响应”的问题时,tcpdump 非常有效。
在 Nginx 服务器执行:
Bashtcpdump -i any tcp port 9000
观察客户端是否真的连接到了 Nginx。
同时可以抓取后端端口:
Bashtcpdump -i any host 10.10.10.20 and port 7000
如果能够看到客户端与 9000 端口建立连接,却完全看不到 Nginx 与 7000 端口建立连接,那么问题大概率集中在 Nginx Stream 配置、后端地址解析或 Nginx 到后端的网络连接上。
如果两个方向都有 TCP 数据包,则可以继续检查应用层协议。
十一、检查SELinux对Socket转发的影响
在 CentOS、RHEL、Rocky Linux、AlmaLinux 等系统中,如果启用了 SELinux,还需要注意安全策略。
查看状态:
Bashgetenforce
如果返回:
Enforcing
可以检查相关日志:
Bashausearch -m AVC -ts recent
某些情况下,Nginx 作为网络代理访问后端 TCP 服务可能受到 SELinux 策略限制。
可以通过日志确认是否存在拒绝记录,而不是直接关闭 SELinux。
生产环境中不建议为了排查问题长期执行:
Bashsetenforce 0
如果临时关闭后问题消失,也应该进一步配置正确的 SELinux 策略,而不是把关闭 SELinux 当作最终解决方案。
十二、Stream代理如何保留客户端真实IP
TCP 代理与 HTTP 代理存在一个重要区别:Stream 默认不会像 HTTP 那样通过 X-Forwarded-For 传递客户端地址。
如果后端 Socket 服务需要知道客户端真实 IP,可以考虑使用 Proxy Protocol。
Nginx:
Nginxstream { server { listen 9000 proxy_protocol; proxy_pass 10.10.10.20:7000; proxy_protocol on; } }
不过这里有一个关键问题:后端服务必须支持 Proxy Protocol。
如果后端程序并不识别 Proxy Protocol,那么 Nginx 添加的 Proxy Protocol 头部可能被后端当成业务数据,从而导致协议解析失败。
因此配置之前必须确认:
Nginx → 后端Socket服务
两端是否约定使用 Proxy Protocol。
十三、Socket长连接异常断开的排查
一些 Socket 服务需要维持数分钟甚至数小时的 TCP 长连接。
例如:
Nginxstream { server { listen 9000; proxy_connect_timeout 10s; proxy_timeout 1h; proxy_pass 10.10.10.20:7000; } }
如果客户端连接一段时间后突然断开,应检查以下几个层面:
-
proxy_timeout是否过短; -
后端程序是否主动关闭连接;
-
防火墙是否存在连接跟踪超时;
-
云负载均衡是否存在空闲连接超时;
-
客户端是否发送心跳;
-
后端 Socket 服务是否存在自身的空闲连接策略。
尤其需要注意,TCP 连接建立成功并不代表可以永久保持。如果中间网络设备存在 idle timeout,而业务又长时间没有数据交换,就可能出现连接被清理的情况。
十四、使用upstream管理多个Socket后端
当 Socket 服务存在多个实例时,可以使用 Stream upstream:
Nginxstream { upstream socket_servers { server 10.10.10.20:7000; server 10.10.10.21:7000; server 10.10.10.22:7000; } server { listen 9000; proxy_connect_timeout 5s; proxy_timeout 300s; proxy_pass socket_servers; } }
这样客户端只需要访问:
Nginx:9000
Nginx 会在后端服务器之间进行连接分配。
对于 TCP 长连接,需要特别理解负载均衡的特点:一次 TCP 连接建立之后,通常会固定对应一个后端连接,并不会把同一个连接中的数据实时切换到另一台服务器。
因此后端服务是否支持连接级负载均衡,需要结合实际业务架构判断。
十五、DNS解析导致的Socket转发问题
如果配置:
Nginxproxy_pass backend.example.com:7000;
需要注意域名解析行为和具体 Nginx 配置方式。
生产环境中,如果后端地址经常变化,仅仅修改 DNS 记录并不一定意味着已经实现了实时动态切换。应根据 Nginx 版本、解析配置以及业务需求设计 DNS 解析策略。
排查时可以先确认:
Bashgetent hosts backend.example.com
或者:
Bashnslookup backend.example.com
再测试:
Bashnc -vz backend.example.com 7000
如果 DNS 解析到了错误地址,Nginx Stream 本身配置正确也无法正常转发。
十六、常见问题快速定位表
| 现象 | 优先检查方向 |
|---|---|
unknown directive "stream" | Stream模块是否安装或加载 |
| Nginx启动失败 | nginx -t、配置层级、模块 |
| 9000端口没有监听 | Stream配置是否被加载 |
| 客户端 Connection refused | 监听端口、防火墙 |
| 客户端 Connection timed out | 安全组、路由、防火墙 |
| Nginx无法连接后端 | 后端服务、端口、防火墙 |
| TCP连接成功但业务无响应 | Socket协议、Proxy Protocol |
| 长连接频繁断开 | proxy_timeout、心跳、网络设备 |
| 后端无法获取真实IP | Proxy Protocol配置 |
| 修改配置后没有效果 | nginx -T确认实际加载配置 |
| 多后端连接异常 | upstream及后端健康状态 |
十七、一套完整的排查流程
遇到 Nginx Stream Socket 转发异常,可以按照下面的顺序处理:
① nginx -t ↓ ② nginx -T ↓ ③ ss -lntp检查Nginx监听端口 ↓ ④ nc测试客户端 → Nginx ↓ ⑤ nc测试Nginx → 后端 ↓ ⑥ ss检查后端监听状态 ↓ ⑦ 检查防火墙和云安全组 ↓ ⑧ 检查SELinux ↓ ⑨ tcpdump抓包 ↓ ⑩ 检查Socket协议和长连接参数
这种排查方式比反复修改 Nginx 配置更加高效。
尤其是:
Bashnginx -t
用于确认配置语法;
Bashnginx -T
用于确认实际生效配置;
Bashss -lntp
用于确认端口监听;
Bashnc -vz
用于确认 TCP 连通性;
Bashtcpdump
用于确认真实网络数据包。
几个命令配合使用,基本可以快速确定故障发生在 Nginx 配置、端口监听、网络策略还是后端 Socket 服务本身。
十八、生产环境配置建议
Nginx Stream 配置虽然看起来简单,但生产环境中仍然需要注意几个细节。
首先,不要随意将所有端口暴露到公网。listen 0.0.0.0:9000 意味着 Nginx 会在所有 IPv4 地址上监听,应结合防火墙和安全组限制访问来源。
其次,长连接业务不要机械地使用很短的 proxy_timeout。应该根据业务心跳周期和网络设备的连接超时策略进行设置。
再次,涉及 Proxy Protocol 时必须确保后端程序支持,否则容易出现“TCP连接成功,但业务协议解析失败”的情况。
最后,修改 Nginx 配置后建议固定执行:
Bashnginx -t && nginx -s reload
这样可以先验证配置,再重新加载服务,避免错误配置直接导致 Nginx 无法启动或重新加载失败。
Nginx Stream 的核心价值并不是简单地“把一个端口转到另一个端口”,而是在四层网络环境中提供稳定的 TCP/UDP 代理能力。实际排查 Socket 转发问题时,应将 Nginx、操作系统防火墙、云安全组、网络链路以及后端 Socket 服务作为一个完整链路进行分析,才能准确定位问题。