背景
Nginx 早期是 HTTP(七层)反代神器,但从 1.9.0(2015 年)开始,ngx_stream_core_module 提供了四层(TCP/UDP)代理——能代理任何 TCP 协议:
- RDP(Windows 远程桌面 3389)
- MySQL、Redis、MongoDB
- SSH
- SMTP、POP3
- 游戏服务器
- 自定义 TCP 协议
与 HAProxy/LVS 对比:
| 维度 | Nginx stream | HAProxy | LVS |
|---|
| 学习成本 | 最低 | 中 | 高 |
| 性能 | 中等 | 强 | 最强 |
| 七层能力 | 同时支持 | 同时支持 | 不支持 |
| 适用规模 | 中小(< 5w QPS) | 大 | 超大 |
典型场景:公司有几台 Windows 服务器,通过一台 Linux 反代把不同端口的 RDP 流量透明转发到不同后端,外网只暴露一个 VIP。
一、Nginx stream 模块基础
1.1 编译参数
stream 模块不是默认编译的,需要 --with-stream:
1
2
3
| nginx -V
# 看是否有 --with-stream
# 如果没有,要重编译
|
重编译步骤:
1
2
3
4
5
6
7
| # 看现有参数
nginx -V
# 在最后加 --with-stream
./configure <原参数> --with-stream
make
# 不要 make install(会覆盖),手动替换二进制
cp objs/nginx /usr/local/nginx/sbin/nginx
|
1.2 配置结构
/etc/nginx/nginx.conf:
1
2
3
4
5
6
7
8
9
10
11
| # 顶层 stream 块(与 http 同级)
stream {
log_format proxy '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'$upstream_bytes_sent $upstream_bytes_received '
'$upstream_connect_time';
# 引入 conf.d/ 下的所有 stream 配置
include /etc/nginx/stream/*.conf;
}
|
/etc/nginx/stream/rdp.conf:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| upstream rdp_pool {
hash $remote_addr consistent; # 同一客户端固定连同一后端
server 10.0.0.10:3389 max_fails=3 fail_timeout=30s;
server 10.0.0.11:3389 max_fails=3 fail_timeout=30s;
}
server {
listen 3390; # 外网端口
proxy_pass rdp_pool; # 转发到 upstream
proxy_connect_timeout 10s;
proxy_timeout 300s;
# 自定义日志
access_log /var/log/nginx/rdp_access.log proxy;
error_log /var/log/nginx/rdp_error.log;
}
|
关键点:
stream 块在顶层(与 http 同级),不在 http 里面- 监听端口不能与
http 块冲突 - 没有
server_name、没有 location(四层只看 IP:Port)
二、MSTSC 远程桌面透传实战
场景:公网 IP 47.104.152.64,Windows 内网 RDP 真实地址 10.0.0.10:3389。
2.1 完整配置
1
2
3
4
5
6
7
8
9
| stream {
log_format proxy '[$time_local] $protocol status:$status, '
'bytes client:$bytes_sent/$bytes_received $session_time, '
'client: $remote_addr, upstream:"$upstream_addr", '
'bytes upstream:$upstream_bytes_sent $upstream_bytes_received '
'$upstream_connect_time';
include /etc/nginx/stream/*.conf;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # /etc/nginx/stream/rdp.conf
upstream mstsc {
hash $remote_addr consistent;
server 10.0.33.101:3389 max_fails=3 fail_timeout=30s;
}
server {
error_log /var/log/nginx/101_3399_error.log;
access_log /var/log/nginx/101_3399_access.log proxy;
listen 3399; # 外网访问 3399
proxy_connect_timeout 10s;
proxy_timeout 300s;
proxy_pass mstsc;
}
|
客户端连接:mstsc /v:47.104.152.64:3399
2.2 日志样例
1
2
3
| [25/Apr/2017:17:55:57 +0800] TCP status:200, bytes client:103/122 10.671,
client: 192.168.3.218, upstream: "192.168.1.176:59001"
bytes upstream:122/103 0.000
|
字段解读:
bytes client:103/122:客户端发送 103 字节,接收 122 字节client: 192.168.3.218:客户端真实 IP(不是 47.104.152.64)upstream: "192.168.1.176:59001":Nginx 主动连到 Windows 的源端口bytes upstream:122/103:Nginx 转发给后端的字节数
三、Nginx HTTP 反代的 9 大 buffer 调优
常见 warning(Nginx error.log):
1
| an upstream response is buffered to a temporary file
|
原因:Nginx 默认的 buffer 太小,每个请求的缓存不够,请求头 header 太大或响应体超过 buffer 时,写入磁盘临时文件,造成 IO 飙升,访问卡顿。
3.1 完整 buffer 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| http {
# client body(上传)
client_max_body_size 2048m; # 最大上传 2GB
client_body_buffer_size 1024k; # body 缓冲 1MB
# proxy buffer
proxy_buffer_size 256k; # header 缓冲 256K
proxy_buffering on; # 开启缓冲(默认 on)
proxy_buffers 64 128k; # 64 个 128K 缓冲(共 8MB)
proxy_busy_buffers_size 512k; # 忙缓冲 512K(不能 > proxy_buffers 总和/2)
# FastCGI buffer(PHP-FPM)
fastcgi_buffer_size 512k; # header 缓冲
fastcgi_buffers 6 512k; # 6 个 512K 缓冲(共 3MB)
fastcgi_busy_buffers_size 512k; # 忙缓冲
fastcgi_temp_file_write_size 512k; # 临时文件写入块大小
fastcgi_intercept_errors on; # 拦截错误(4xx/5xx 自己处理)
}
|
3.2 各项调优解释
| 配置 | 作用 | 调优建议 |
|---|
client_max_body_size | 最大请求体(POST 上传) | 按业务定,文件上传建议 1g+ |
client_body_buffer_size | 客户端 body 缓冲 | 16k ~ 1m |
proxy_buffer_size | 后端响应头缓冲 | 4k ~ 32k,大响应头可调到 128k |
proxy_buffers | 后端响应体缓冲(数量 × 单个大小) | 通常 8 16k 或 64 128k |
proxy_busy_buffers_size | 正在发送的缓冲上限 | 必须 < proxy_buffers 总和的 1/2 |
proxy_temp_file_write_size | 临时文件单次写入大小 | 默认 8k,调大可减少 IO 次数 |
fastcgi_intercept_errors | Nginx 拦截 FastCGI 错误 | on 配合 error_page 自定义错误页 |
3.3 调优公式
总 buffer = proxy_buffer_size + proxy_buffers × 数量
例:proxy_buffers 64 128k = 64 × 128K = 8MB 单连接缓存
繁忙 buffer = proxy_busy_buffers_size × 2 ≤ 总 buffer
例:512k × 2 = 1MB ≤ 8MB ✓
四、Connection reset by peer 排错
完整错误:
1
2
3
4
| recv() failed (104: Connection reset by peer)
while proxying and reading from upstream,
client: 89.248.163.87, server: 0.0.0.0:3399,
upstream: "10.8.33.101:3389"
|
104 错误 = TCP RST,连接被对端强制关闭。
常见原因:
- 后端服务主动断开(如 Windows RDP 服务异常)
- 后端超时未响应,TCP keepalive 触发 RST
- 后端资源耗尽(CPU 100%、连接数打满)
- Nginx 与后端的网络问题(中间设备丢包)
4.1 调优配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| upstream bigdata {
server 10.0.20.1:18018;
server 10.0.20.2:18018;
server 10.0.20.3:18018;
server 10.0.20.4:18018;
keepalive 100; # Nginx 与上游保持的长连接数
}
location / {
proxy_pass http://bigdata;
# 加大超时(默认 60s 偏短)
proxy_connect_timeout 120;
proxy_send_timeout 120;
proxy_read_timeout 120;
# 关键:启用 HTTP/1.1 + 清理 Connection header
proxy_http_version 1.1;
proxy_set_header Connection "";
}
|
关键点:
| 配置 | 作用 |
|---|
keepalive 100 | Nginx 与每个上游保持 100 个长连接(连接复用) |
proxy_http_version 1.1 | 必须 1.1——HTTP/1.0 不支持 keep-alive |
proxy_set_header Connection "" | 清掉 Connection: close,让 keep-alive 生效 |
为什么 HTTP/1.1 + Connection "" 必加?
HTTP/1.0 的 Connection: keep-alive 是可选的,HTTP/1.1 默认 keep-alive 但 Connection: close 可以强制关闭。Nginx 默认往上游发的 Connection: close(不重用连接),所以必须显式清掉。
五、实战:MySQL stream 代理
1
2
3
4
5
6
7
8
9
10
11
12
13
| stream {
upstream mysql_pool {
server 10.0.0.10:3306;
server 10.0.0.11:3306;
}
server {
listen 33306;
proxy_pass mysql_pool;
proxy_connect_timeout 5s;
proxy_timeout 600s;
}
}
|
客户端连接:
1
| mysql -h <nginx-ip> -P 33306 -u root -p
|
注意:MySQL 认证是基于 IP 的,代理后所有客户端都是 nginx-ip,可能触发 host 'nginx-ip' is not allowed。对策:
- MySQL 授权时用
'root'@'%' - 或在 MySQL 端用
skip-name-resolve
六、实战:Redis stream 代理
1
2
3
4
5
6
7
8
9
10
11
12
| stream {
upstream redis_pool {
server 10.0.0.10:6379;
}
server {
listen 36379;
proxy_pass redis_pool;
proxy_connect_timeout 5s;
proxy_timeout 300s;
}
}
|
Redis 协议简单,stream 代理几乎零问题。
七、SSL/TLS over stream
stream 块也支持 SSL(需 --with-stream_ssl_module):
1
2
3
4
5
6
7
8
9
10
11
12
13
| stream {
upstream mysql_ssl {
server 10.0.0.10:3306;
}
server {
listen 33307 ssl;
proxy_pass mysql_ssl;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
}
}
|
典型场景:MySQL 客户端启 SSL,但不想让客户端直接连真实 MySQL,走 Nginx 反代统一证书。
八、负载均衡算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| upstream backend {
# 1. 轮询(默认)
# server 10.0.0.10:8080;
# server 10.0.0.11:8080;
# 2. 加权轮询
# server 10.0.0.10:8080 weight=3;
# server 10.0.0.11:8080 weight=1;
# 3. ip_hash(会话保持)
# ip_hash;
# server 10.0.0.10:8080;
# server 10.0.0.11:8080;
# 4. consistent hash(一致性 hash,加减节点影响小)
hash $remote_addr consistent;
server 10.0.0.10:8080;
server 10.0.0.11:8080;
# 5. least_conn(最闲)
# least_conn;
# server 10.0.0.10:8080;
# 6. random
# random;
}
|
九、健康检查与失败处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| upstream backend {
server 10.0.0.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
# 健康检查:失败 3 次标记为 down,30 秒后再试
}
server {
listen 80;
proxy_pass backend;
proxy_next_upstream error timeout invalid_header http_502 http_503;
# 后端 error/timeout/502/503 时,自动切到下一个 upstream
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;
}
|
max_fails 与 fail_timeout:
- 30 秒内失败 3 次 → 标记 down
- 30 秒后再尝试(如果还失败,继续 down)
proxy_next_upstream:
error:连接错误timeout:超时invalid_header:响应头无效http_502/503/504:HTTP 错误码off:禁用
十、常见错误
10.1 stream 块识别为 HTTP
症状:Nginx 启动报 unknown directive "stream"。
原因:编译时没加 --with-stream。
对策:重编译 nginx 加 --with-stream,参考 1.1 节。
10.2 listen 端口冲突
1
| bind() to 0.0.0.0:3399 failed (98: Address already in use)
|
对策:
1
2
3
| ss -tan | grep 3399
# 看哪个进程占用
lsof -i :3399
|
10.3 客户端真实 IP 丢失
默认情况:后端看到的客户端 IP 是 Nginx 内网 IP(不是真实客户端)。
对策(仅限 HTTP,TCP 协议做不到):
1
2
3
4
5
| location / {
proxy_pass http://backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
|
stream 块没有 proxy_set_header!要传真实 IP 必须用应用层协议(如 HTTP 的 X-Forwarded-For、MySQL 的账号 IP 白名单)。
10.4 大量 TIME_WAIT
症状:netstat -an | grep TIME_WAIT | wc -l 几万。
对策:
1
2
3
4
5
| # 开启 TIME_WAIT 复用
sysctl -w net.ipv4.tcp_tw_reuse=1
# 缩短 TIME_WAIT 时间(默认 60s)
sysctl -w net.ipv4.tcp_fin_timeout=30
|
小结
Nginx stream 模块让 Nginx 从七层反代进化为"四/七层通吃":
- 编译加
--with-stream stream 块与 http 同级listen + proxy_pass 简单粗暴keepalive + HTTP/1.1 + Connection "" 是 HTTP 反代性能关键buffer 调优避免写临时文件- 客户端真实 IP(四层)传不到后端,靠应用层协议
生产建议:
- 七层反代:用 Nginx,配置友好
- 四层 1w-5w QPS:用 Nginx stream
- 四层 5w+ QPS:用 LVS DR 或 HAProxy
- 多协议混合:Nginx 统一管(HTTP 在 http 块,TCP 在 stream 块)
下一步:
- 用 Nginx stream + Let’s Encrypt 做 TLS 反代
- 上 HAProxy 做更细的健康检查
- 用 Envoy / APISIX 做云原生时代的四/七层代理
2024 视角:Nginx 仍是 1.25+ / 1.27+ 时代的王者
2013 那篇的 ngx_stream_core_module 仍是 Nginx 的核心特性。2024 视角下:
一、Nginx 1.25 / 1.27(2024 主流)
- Nginx 1.25.x(2023-05 起的 mainline):实验新特性。
- Nginx 1.27.x(2024-08):新 mainline,引入 HTTP/3 over QUIC 完整支持。
- Nginx Plus R32(2024):商业版新增 Dynamic Configuration via API、Active Health Checks 增强。
1
2
3
| # 看版本
nginx -v
# nginx version: nginx/1.27.0
|
二、QUIC / HTTP/3 已成"必选项"
- 2013 那篇完全没提 QUIC。2024 视角下 QUIC / HTTP/3 已是性能优化标配:
- 握手 0-RTT(比 TLS 1.3 还快)
- 连接迁移(IP 变化不重连)
- 多路复用(解决 HTTP/2 队头阻塞)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # Nginx 1.25+ 启用 HTTP/3
server {
listen 443 ssl;
http2 on;
# 启用 HTTP/3(同时监听 UDP 443)
add_header Alt-Svc 'h3=":443"; ma=86400';
ssl_protocols TLSv1.2 TLSv1.3;
ssl_early_data on;
ssl_protocols TLSv1.3;
}
# Nginx 1.27+ 直接
server {
listen 443 quic reuseport;
listen 443 ssl;
http2 on;
add_header Alt-Svc 'h3=":443"; ma=86400';
}
|
三、buffer 调优的"现代"配方
- 2013 那篇给的
proxy_buffer_size 256k / proxy_buffers 64 128k 仍是有效。 - 2024 实践:
1
2
3
4
5
6
7
8
9
10
| # 大文件 / 视频流
proxy_buffer_size 1m;
proxy_buffers 8 1m; # 8 个 1M(共 8M)
proxy_busy_buffers_size 2m;
proxy_temp_file_write_size 4m;
# API 服务(小响应)
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
|
- 新指令:
proxy_socket_keepalive on:向上游发 TCP keep-alive(避免 TIME_WAIT 堆积)proxy_pass_request_body off:透传大 body 优化proxy_force_ranges on:强制 Range 支持
四、Connection reset by peer 的 2024 排查清单
2013 那篇给的 proxy_http_version 1.1 + Connection "" + keepalive 100 仍是标准动作。2024 升级:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| upstream backend {
server 10.0.20.1:18018 resolve;
server 10.0.20.2:18018 resolve;
keepalive 32; # 适当调小(旧值 100 过大)
keepalive_requests 1000; # 单连接最多 1000 请求后关闭(防内存泄漏)
keepalive_timeout 60s;
}
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 5s; # 比 120s 短
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_socket_keepalive on; # 2024 新
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 2;
}
|
- 新调试工具:
nginx -T:dump 完整配置(include 全部展开)error.log debug:详细 debug 日志stub_status:基础统计/usr/lib/nginx/modules/ngx_http_vhost_traffic_status(淘宝开源):流量详情
五、stream 块在 K8s 时代的"角色"
- 2024 趋势:Nginx stream 在 K8s 时代被 Envoy 替代。
- Nginx Ingress Controller(K8s 标准 Ingress 实现):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # k8s-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
|
- 优势:Nginx Ingress Controller = Nginx + K8s 自动化(自动 reload、自动证书、自动 upstream)。
六、动态 upstream 的"现代"实现
- 2013 那篇的 upstream 是静态配置。
- 2024 现代姿势:
upstream {} 动态解析(DNS + Service Discovery):
1
2
3
4
5
6
7
8
9
10
| # 动态 upstream(Nginx Plus 特性,开源版部分支持)
upstream backend {
zone backend 64k;
server backend-service.default.svc.cluster.local:80 resolve;
resolver kube-dns.kube-system.svc.cluster.local valid=30s;
}
# 通过 API 动态加 backend
curl -X POST http://127.0.0.1:8080/api/version/http/upstreams/backend/servers/ \
-d '{"server":"10.0.1.50:80"}'
|
- Nginx 开源版(1.27+)也支持
resolver + resolve 指令,SVC 后端自动发现。
七、SSL/TLS 的"现代"最佳实践
- 2013 那篇的
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 已经被2024 安全规范淘汰。 - 2024 强制:
1
2
3
4
5
6
7
8
| ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
|
- OCSP Stapling:减少客户端验证延迟
- TLS 1.3 强制:禁用 TLS 1.0/1.1
八、Nginx + 服务网格的"混合部署"
- 2024 大量 K8s 项目不再用 Nginx 反代——用 Istio / Linkerd 自带 Envoy。
- Nginx 仍在用的场景:
- 边缘节点(CDN 边缘)
- 物理机 / 虚拟机(不在 K8s 里)
- Web 服务器(静态资源)
- API 网关(Apigee / Kong 基于 Nginx)
- Sidecar 替代(轻量场景,Nginx sidecar 比 Envoy 资源占用更少)
九、Nginx 商业替代
- F5 NGINX Plus(商业版):Dynamic Configuration API、Active Health Checks、JWT 验证、WAF
- OpenResty(章亦春):Lua 脚本扩展——2024 仍是国内"高阶 Nginx 玩法"主流
- Caddy:自动 HTTPS(Let’s Encrypt 内置)
- Traefik:云原生时代的"动态 Nginx"
源文档:
os/linux/第三方tools/dev/反向代理/Nginx/nginx.md(stream TCP 代理 + 自定义日志)os/linux/第三方tools/dev/反向代理/Nginx/问题.md(buffer 调优 + Connection reset by peer)