很多对 Nginx 的认知还停留在 HTTP 反向代理这一层,实际上 Nginx 在 1.9.0 版本中就已经引入了 stream 模块,专门用来处理 TCP 和 UDP 的四层转发。这篇文章不讨论“给 Nginx 某个服务做负载均衡”这种老生常谈,而是把 stream 模块当作一个独立能力来拆解:它解决什么问题、配置长什么样、TCP 和 UDP 的差异在哪里、实测会遇到哪些坑,以及我把数据库入口、内部 DNS 转发接入这套体系后的真实心得。
1. 藏在 Web 服务器里的四层代理:先搞清楚你究竟要转发什么
在动手配 Nginx 之前,我建议先别急着写配置。你要想明白一件事:TCP/UDP 代理和 HTTP 反向代理,看上去都是在做“转发”,但它们的工作层级完全不同。HTTP 反向代理跑在七层,能看懂 Host、URI、Cookie、Header,所以可以做路由、限流、缓存、改写。而 stream 模块跑在四层,它只负责把内核收到的 TCP 连接或 UDP 报文按策略转给后端,不关心包里面装的到底是什么应用协议。
这意味着什么?意味着你准备用 Nginx stream 代理解析过的 MySQL 流量,那就只能在连接层做转发,没法认库表级别去拦 SQL;想给 Redis 做代理,就只能基于 IP、端口、源地址做策略,不存在“key 开头是某个前缀就转发到某节点”的说法。四层代理的边界就是把连接送对、把流量传稳、把后端状态管好,超过这个范围的需求,应该回到七层去解决。
1.1 TCP 是“会话式”的,UDP 是“报文式”的,代理逻辑完全不同
TCP 代理相对好理解:客户端通过三次握手与 Nginx 建立一条连接,Nginx 再通过三次握手与后端建立一条连接,两条连接建立完成后,Nginx 就负责把客户端发来的字节流搬到后端,再把后端返回的字节流搬回客户端。这个过程本质上是连接中转,Nginx 不需要知道传输的内容是 SQL、Redis 命令还是自定义 RPC。
UDP 就不同了。UDP 本身没有连接状态,客户端发一个数据报,Nginx 需要自己维护一个伪会话,才能决定这个报文要转发给哪个后端,以及后端返回的报文要还给哪个客户端。这个伪会话的 key 一般是五元组:源地址、源端口、目的地址、目的端口、协议类型。只要客户端后续报文还是相同五元组,Nginx 就认为是在同一个 UDP 会话里,会把报文送往同一个后端。这个特性非常关键,后面配置 UDP 代理时会直接影响参数选择。
1.2 什么时候不该硬上 Nginx 来做四层转发?
Nginx 的 stream 模块当然不是万能的。如果公司已经有 LVS、HAProxy 这一套独立四层入口,或者业务 PPS 已经高到需要 DPDK/专用网关,那没必要为了“统一技术栈”强行把流量接到 Nginx 上。Nginx stream 的优势是配置语义丰富、跟现有 Nginx 体系能共用一套监控和日志方案、部署成本低;劣势是单机转发吞吐和 PPS 不如专用的内核态方案,尤其是在小包场景下差距会更加明显。
我个人的使用边界是:内部服务的入口收敛、测试环境流量调度、中小规模集群的 TCP/UDP 负载均衡,这类场景用 Nginx stream 完全够用;如果公司流量已经跑到需要单独规划网关机器硬件、要做 DPDK 优化的级别,那该上专用方案就上专用方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与第一份配置:先确认 Nginx 有 stream 能力,再谈转发
很多人在配置过程中踩坑,第一步就出在 Nginx 编译参数上。stream 模块并不是所有发行版默认编译进 Nginx 的。你拿系统自带的 nginx 包执行 nginx -V,可能在 configure arguments 里根本没有 --with-stream,这时候后面的所有配置都不会生效。
2.1 当前 Nginx 是否具备 TCP/UDP 代理能力,一分钟自查
在终端执行:
bash复制nginx -V 2>&1 | tr ' ' '\n' | grep stream
如果输出里能看到 --with-stream 和 --with-stream_ssl_module,说明当前 Nginx 支持 TCP/UDP 代理。如果没有任何输出,你就要考虑两个解决方案:一是重新编译 Nginx,在 configure 阶段加入相应模块;二是改用 nginx.org 官方仓库提供的预编译包,官方主线版本通常会带上 stream 模块。
需要特别提醒的是:如果你的 Nginx 是通过 load_module 方式加载动态模块,那要确认是否加载了 ngx_stream_module.so。有的发行版把四层模块拆成了独立动态模块,没有主动加载,主配置文件里写 stream 块时会直接报出“unknown directive stream”之类的错误。
2.2 stream 块和 http 块是平级关系,不能写在 http 块里面
这个错误我见了不少次:有人把 stream 配置写进 HTTP 站点配置文件里,结果问题迟迟找不出来。stream 块在 Nginx 主配置文件中与 http 块、events 块平级,它不是 http 的子级。一个最小可用的 TCP 转发配置是这样的:
nginx复制stream {
upstream backend_tcp {
server 192.168.1.11:3
