Nginx 四层代理 stream

前面章节的 http 块工作在七层,能读懂 URL、Host、Cookie 并做缓存改写;而 MySQL、Redis、DNS 这类协议 Nginx 无法解析内容,只能做四层代理把 TCP/UDP 流量原样转发。stream 模块就是干这个的。

stream 与 http 的区别

http 七层与 stream 四层对比

对比项http 模块stream 模块
工作层次七层(应用层)四层(传输层)
能看到的URL、Host、Header、Cookie只有 IP 与端口
典型用途网站、API 反向代理数据库、缓存、消息队列、DNS 转发
配置位置http { } 块stream { } 块(与 http 平级的顶层块)

stream 默认不编译,需要 --with-stream,可用 nginx -V 确认:

nginx -V 2>&1 | grep -o 'with-stream'
# 输出:with-stream    (无输出则需重新编译)

TCP 转发与四层负载均衡

把本机 3306 转发到内网 MySQL,或对一组后端做 TCP 轮询:

stream {
    upstream mysql_pool {
        server 10.0.0.11:3306;
        server 10.0.0.12:3306;
    }
    server {
        listen 3306;              # 对外监听端口
        proxy_pass mysql_pool;    # 转发到 upstream 组(四层负载均衡)
        proxy_timeout 30s;        # 两次读写之间的超时
    }
    server {
        listen 6380;
        proxy_pass 127.0.0.1:6379;    # 简单转发到本机 Redis
    }
}
mysql -h 网关IP -P 3306 -u app -p    # 客户端像连数据库一样连 Nginx
redis-cli -h 网关IP -p 6380 ping     # 输出:PONG

SSL 透传

后端是 HTTPS/TLS 服务时,可以让 Nginx 不解密、原样转发密文,证书在后端维护,转发开销最小:

stream {
    server {
        listen 443;
        proxy_pass 10.0.1.2:443;    # 密文透传,Nginx 不参与 TLS 握手
    }
}

若想在一台机器上按域名把 443 分流到不同后端,可编译 --with-stream_ssl_preread_module,用 SNI 路由(简提):

stream {
    map $ssl_preread_server_name $backend {
        a.example.com 10.0.1.2:443;
        b.example.com 10.0.1.3:443;
    }
    server {
        listen 443;
        proxy_pass $backend;      # 按 SNI 域名选择后端
        ssl_preread on;
    }
}

UDP 转发

给监听加 udp 参数即可转发 UDP,典型场景是内网 DNS 网关:

stream {
    server {
        listen 53 udp reuseport;      # reuseport 提升多 worker 收包性能
        proxy_pass 223.5.5.5:53;      # 转发到公共 DNS
    }
}
nslookup www.example.com 网关IP     # 通过 Nginx 出口查 DNS

小结

stream 是与 http 平级的顶层配置块:编译带 --with-stream 后,在 stream {} 里 listen 端口 + proxy_pass 目标即可完成 MySQL/Redis 等 TCP 转发、UDP 转发与 SSL 透传。记住"能读懂 URL 用 http、只认端口用 stream"即可选型无忧。

笔记加载中…