Nginx TCP/UDP四层转发(Stream模块)完整深入教程

一、基础原理

Nginx分为七层HTTP模块(http{})四层流模块(stream{})

  • http{}:解析HTTP协议,处理http/https、反向代理、静态资源
  • stream{}传输层四层代理,不对数据包做协议解析,纯粹透明转发TCP连接、UDP数据包

支持业务:MySQL、Redis、SSH、DNS、RDP、游戏端口、syslog、VPN端口、WebSocket、MQTT等所有TCP/UDP服务

版本门槛:nginx ≥ 1.9.13 正式支持stream模块

核心特点

  1. TCP:面向连接,Nginx维持客户端‑Nginx‑后端两条TCP连接
  2. UDP:无连接数据包,Nginx维护会话哈希表,相同五元组数据包转发至同一个后端

⚠️ 硬性语法:stream{} 必须和 http{} 同级,不能写在http大括号里面


二、检查Nginx是否支持stream模块

1、查看编译参数

bash 复制代码
nginx -V 2>&1 | grep stream
  • 输出 --with-stream → 已经内置静态模块,可以直接使用
  • 输出 --with-stream=dynamic → 动态模块,需要手动加载
  • 无任何输出 → 当前nginx不带stream模块

2、安装带stream模块的nginx

bash 复制代码
# CentOS/RHEL
yum install nginx-mod-stream -y

# Ubuntu/Debian
apt install nginx-full -y

动态模块加载方式(nginx.conf最顶部)

nginx 复制代码
load_module modules/ngx_stream_module.so;

三、最简入门示例

打开主配置文件 nginx.conf

3.1 TCP端口转发示例

本机3307端口,转发到后端MySQL 192.168.1.100:3306

nginx 复制代码
stream {
    server {
        listen 3307;
        proxy_pass 192.168.1.100:3306;
    }
}

3.2 UDP端口转发示例(DNS 53端口)

listen后面必须添加 udp 参数,否则默认监听TCP

nginx 复制代码
stream {
    server {
        listen 53 udp;
        proxy_pass 223.5.5.5:53;
    }
}

重载配置生效

bash 复制代码
nginx -t
nginx -s reload

四、Upstream上游集群 & 负载均衡策略

适合多台后端服务器做四层负载均衡,TCP、UDP均可共用upstream

4.1 基础上游组

nginx 复制代码
stream {
    upstream mysql_group {
        server 192.168.1.10:3306 weight=2;
        server 192.168.1.11:3306 weight=1;
        server 192.168.1.12:3306 backup; # 备用节点,全部主节点宕机才启用
    }

    server {
        listen 3306;
        proxy_pass mysql_group;
    }
}

server后端参数说明

参数 作用
weight=N 权重,默认1;数值越高分配流量越多
backup 备份节点,平时不接收流量
down 标记节点永久下线
max_fails=3 失败3次判定后端故障
fail_timeout=30s 故障之后30s不再探测该后端

4.2 五种负载均衡算法

写在upstream块内部

nginx 复制代码
upstream backend {
    #1.轮询(默认,无需配置)
    server 127.0.0.1:8080;

    #2.最少连接,优先分配给当前活跃连接最少后端,长连接业务首选MySQL/Redis
    least_conn;

    #3.IP哈希,同一个客户端IP固定转发一台后端,实现会话黏连
    ip_hash;

    #4.最短响应时间(Nginx‑Plus商业版)
    least_time first_byte;

    #5.自定义哈希
    hash $remote_addr$remote_port consistent;
}

UDP无连接短包业务:优先轮询;TCP长连接数据库优先 least_conn


五、listen 完整高级参数详解

nginx 复制代码
listen  [ip]:port [参数列表];

常用全部参数:

nginx 复制代码
# TCP示例
listen 0.0.0.0 2222 reuseport so_keepalive=on backlog=10000 proxy_protocol;

# UDP示例
listen 0.0.0.0:53 udp reuseport;
  1. udp:开启udp数据包代理,没有就是tcp
  2. reuseport 生产环境必加

Linux内核多端口复用,多个worker进程独立监听端口,减少锁竞争,高并发性能暴涨

  1. so_keepalive=on
    开启TCP保活,Nginx自动发送keepalive探测空闲断开的死连接,适合SSH、远程桌面长连接
  2. backlog=数值
    TCP挂起连接队列上限,高并发调大至10000‑65535
  3. proxy_protocol
    开启PROXY‑PROTOCOL协议;Nginx接收上层负载均衡器传来的真实客户端IP
  4. ipv6only=on 监听IPv6端口
nginx 复制代码
listen [::]:3306;

六、stream代理核心指令大全(生产必配置)

6.1 超时参数(重中之重,绝大多数连接异常都是超时不合理)

nginx 复制代码
server {
    listen 3307;
    proxy_pass mysql_group;

    # 和后端建立TCP连接超时,TCP专用;UDP不需要握手,该参数无效
    proxy_connect_timeout 3s;

    # 空闲超时:两端长时间没有任何数据,则关闭代理会话
    # MySQL长连接设置600s;DNS/UDP短包设置10s
    proxy_timeout 300s;
}

6.2 缓冲区配置

nginx 复制代码
# 数据包缓冲区大小;协议握手包很大时上调至64k
proxy_buffer_size 16k;

6.3 后端故障转移

nginx 复制代码
# 连接失败、超时的时候自动切换下一台后端
proxy_next_upstream on;
# 故障转移最长耗时
proxy_next_upstream_timeout 10s;
# 最多尝试后端数量
proxy_next_upstream_tries 3;

6.4 UDP专属指令

nginx 复制代码
server {
    listen 53 udp;
    proxy_pass 223.5.5.5:53;

    # 等待后端返回UDP响应包数量
    # 0=单向流量,不需要等待后端回复(syslog日志上报)
    proxy_responses 1;
    # UDP会话超时,无数据包之后销毁会话表
    proxy_timeout 10s;
}

UDP原理:nginx维护一张五元组会话表;同一个客户端ip+port数据包路由至固定后端;proxy_timeout到期销毁会话。

6.5 客户端真实IP透传后端

四层TCP代理默认后端获取到的是Nginx内网IP;有两种方案

方式1:Proxy‑Protocol(推荐,MySQL/Redis/SSH全部支持)

nginx侧:

nginx 复制代码
listen 3306 proxy_protocol;

然后后端服务开启proxy protocol解析客户端源IP

方式2(仅Nginx商业版)

proxy_set_header 只属于七层http模块,stream四层不能使用!


七、访问日志、错误日志配置

stream模块日志需要独立定义日志格式,和http日志互不通用

nginx 复制代码
stream {
    # 自定义日志格式
    log_format stream_log '$remote_addr [$time_local] $protocol '
    '$status $bytes_sent $bytes_received $session_time';

    # 全局访问日志
    access_log /var/log/nginx/stream-access.log stream_log buffer=64k;
    error_log  /var/log/nginx/stream-error.log warn;

    server {
        listen 3306;
        proxy_pass 192.168.1.10:3306;
    }
}

日志字段解释

  • $protocol:TCP / UDP
  • $status:会话状态
  • $bytes_sent:nginx发给客户端字节
  • $bytes_received:nginx收到客户端字节
  • $session_time:连接存活时长

八、四层SSL透传 TLS‑Passthrough(HTTPS/SSL流量直接转发不解密)

适合后端自己处理SSL证书,Nginx只做四层流量转发、不做证书解密

nginx 复制代码
stream {
    upstream https_backend {
        server 192.168.1.10:443;
    }
    server {
        listen 443;
        proxy_pass https_backend;
        proxy_connect_timeout 5s;
        proxy_timeout 3600s;
    }
}

如果需要Nginx负责SSL加解密,则需要 proxy_ssl 系列指令,属于Nginx高级TLS代理。


九、完整生产环境优化配置模板

nginx.conf主配置

nginx 复制代码
# 开启stream动态模块(静态编译则删掉此行)
#load_module modules/ngx_stream_module.so;

worker_processes auto;
worker_rlimit_nofile 200000; # 文件句柄上限

events {
    worker_connections 65535;
    multi_accept on;
}

http {
    # 此处放普通http网站配置,和四层代理互不干扰
}

# ==========四层TCP/UDP转发全部在这里==========
stream {
    log_format stream_log '$remote_addr [$time_local] $protocol '
    '$status $bytes_sent $bytes_received $session_time';
    access_log /var/log/nginx/stream-access.log stream_log buffer=128k;
    error_log /var/log/nginx/stream-error.log warn;

    # MySQL集群
    upstream mysql_cluster {
        least_conn;
        server 192.168.1.20:3306 max_fails=3 fail_timeout=20s;
        server 192.168.1.21:3306 max_fails=3 fail_timeout=20s;
    }
    server {
        listen 3306 reuseport so_keepalive=on backlog=20000;
        proxy_pass mysql_cluster;
        proxy_connect_timeout 3s;
        proxy_timeout 600s;
        proxy_buffer_size 32k;
    }

    # Redis TCP代理
    server {
        listen 6379 reuseport;
        proxy_pass 192.168.1.30:6379;
        proxy_connect_timeout 2s;
        proxy_timeout 300s;
    }

    # DNS UDP 53端口
    server {
        listen 53 udp reuseport;
        proxy_pass 223.5.5.5:53;
        proxy_responses 1;
        proxy_timeout 8s;
    }

    # SSH端口转发
    server {
        listen 2222 reuseport so_keepalive=on;
        proxy_pass 192.168.1.50:22;
    }
}

十、内核&操作系统性能优化(四层代理高并发必备)

1. sysctl.conf 内核参数优化

ini 复制代码
# 打开端口复用、TIME‑WAIT快速回收
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0
net.core.somaxconn = 65535
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

生效:sysctl -p

2、防火墙放行端口

bash 复制代码
# firewalld
firewall-cmd --add-port=3306/tcp --permanent
firewall-cmd --add-port=53/udp --permanent
firewall-cmd --reload

十一、常见问题排查清单

1、启动报错 unknown directive "stream"

原因:nginx没有stream模块

  • yum安装nginx‑mod‑stream 或者加载动态模块

2、nginx可以ping通后端,但连接端口超时

  1. Nginx服务器防火墙拦截出站流量
  2. 后端服务器防火墙、安全组没有放行端口
  3. 后端服务仅监听127.0.0.1,没有监听0.0.0.0

检查后端监听:ss -tulnp | grep 3306

3、UDP数据包收不到、时通时断

  1. listen必须带上udp关键字
  2. 缩短proxy_timeout,UDP会话超时销毁时间建议5‑15s
  3. 安全组同时放行udp协议

4、长连接过一会自动断开

调大 proxy_timeout;TCP开启 so_keepalive=on

5、负载均衡策略不生效

  • least_conn 只针对TCP长连接有效;DNS这类一次性UDP短包几乎看不出效果
  • ip_hash 依靠客户端IP,经过NAT之后所有客户端IP一致会造成流量倾斜

6、需要获取真实客户端IP

使用 listen xxx proxy_protocol;,后端开启proxy‑protocol支持


十二、stream和HAProxy、LVS简单对比

  1. Nginx‑Stream:配置简单,可以七层+四层一体化,适合中小型业务、混合协议网关;缺点高性能弱于LVS
  2. HAProxy:四层七层都很强,健康检查功能完善,适合数据库负载均衡
  3. LVS:纯内核态四层负载,性能最强,适合几十万以上超高并发UDP/TCP流量

适用场景总结

  • Redis、MySQL、MQTT、SSH、RDP → Nginx‑TCP转发
  • DNS、syslog日志、游戏数据包、语音数据包 → Nginx‑UDP转发
  • 四层SSL透传、端口网关、多端口统一入口
相关推荐
夏雪coding10 小时前
nginx SPA 回落把 CSS 变成了 HTML:一个返回 200 却让样式失效的坑
前端·nginx
難釋懷11 小时前
Nginx日志
前端·网络·nginx
老赵的博客12 小时前
工控机之UDP组播
网络·网络协议·udp
fhqlongteng13 小时前
TCP服务器断开客户端的方法
服务器·网络·tcp/ip
Lysander.Jovian15 小时前
Nginx服务2
运维·数据库·nginx
风曦Kisaki16 小时前
# Kubernetes(K8s)笔记Day15:K8s网络工作原理【Pod 网络模型,CNI 网络插件,Calico 工作模式详解,BGP协议,路由反射器】
运维·网络·nginx·云原生·容器·kubernetes
NeilYuen1 天前
用UDP实现向DNS服务器请求IP地址
服务器·tcp/ip·udp
shengnan_wsn1 天前
【协议】【TCP】
网络·网络协议·tcp/ip
消失的旧时光-19431 天前
第 3 篇:机器人 TCP 长连接如何稳定运行?心跳、掉线检测与自动重连
网络协议·tcp/ip·机器人