lolly/docs/10-nginx-stream-tcp-udp.md
xfy d8ac807cb7 docs(nginx): 更新代理与 stream 文档,新增 njs/可观测性/ACME 指南
- 04: 新增 random 负载均衡、upstream 响应时间变量详解
- 10: 新增访问控制、连接限制、地理/真实IP模块、高级日志配置
- 24: 新增 worker_aio_requests、EPOLLEXCLUSIVE 详解
- 30: njs JavaScript 模块完整指南
- 31: OpenTelemetry 可观测性集成指南
- 32: ACME 自动证书管理指南

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-07 17:06:38 +08:00

22 KiB
Raw Blame History

NGINX TCP/UDP Stream 模块指南

1. Stream 模块概述

Stream 模块提供 TCP/UDP 流处理功能,包括:

  • TCP 代理与负载均衡
  • UDP 代理DNS、日志收集等
  • TLS/SSL 终端
  • 基于 SNI 的路由

版本要求

  • 自 1.9.0 版本可用
  • 默认不构建,需编译时添加 --with-stream 参数

配置上下文

stream {
    # TCP/UDP 配置
    server {
        listen 12345;
        proxy_pass backend:54321;
    }
}

2. 基础配置示例

TCP 代理

stream {
    upstream backend {
        server 192.168.1.1:3306;
        server 192.168.1.2:3306;
    }

    server {
        listen 3306;
        proxy_pass backend;
        proxy_timeout 3s;
        proxy_connect_timeout 1s;
    }
}

UDP 代理

stream {
    upstream dns_servers {
        server 8.8.8.8:53;
        server 8.8.4.4:53;
    }

    server {
        listen 53 udp;
        proxy_pass dns_servers;
        proxy_timeout 20s;
    }
}

Unix Socket 代理

stream {
    server {
        listen unix:/tmp/stream.sock;
        proxy_pass unix:/tmp/backend.sock;
    }
}

3. 负载均衡

配置示例

stream {
    upstream mysql_backend {
        hash $remote_addr consistent;  # 一致性哈希
        server mysql1:3306 weight=5;
        server mysql2:3306;
        server mysql3:3306 backup;
    }

    server {
        listen 3306;
        proxy_pass mysql_backend;
    }
}

负载均衡算法

算法 指令 说明
轮询 默认 加权轮询
最少连接 least_conn; 连接数最少优先
哈希 hash key [consistent]; 基于键哈希
最少时间 least_time header | last_byte | last_byte inflight; 最小响应时间NGINX Plus
随机 random [two] [least_conn]; 随机选择

server 参数

参数 说明
weight=N 权重
max_conns=N 最大连接数
max_fails=N 失败次数阈值
fail_timeout=T 失败统计时间
backup 备份服务器
down 标记不可用
resolve 解析域名 IP 变化

新负载均衡算法

least_time最小响应时间

版本要求NGINX Plus

upstream mysql_backend {
    least_time header;  # 或 last_byte, last_byte inflight
    server 192.168.1.1:3306;
    server 192.168.1.2:3306;
    zone mysql 64k;
}

参数说明

参数 说明
header 以接收到上游第一个字节的时间为度量
last_byte 以接收到上游完整响应的时间为度量
last_byte inflight 考虑正在传输的数据

random随机选择

upstream backend {
    random;                    # 纯随机
    # random two;              # 随机选两个,按权重选
    # random two least_conn;   # 随机选两个,选连接少的
    server 192.168.1.1:3306;
    server 192.168.1.2:3306;
}

参数说明

参数 说明
two 随机选择两个上游服务器,再根据负载均衡策略选择
least_conn two 配合,选择连接数较少的

upstream zone 共享内存

zone 指令启用 upstream 配置的共享内存,多 worker 进程间共享连接状态:

upstream backend {
    zone backend 64k;  # 64k 共享内存
    server 192.168.1.1:3306;
    server 192.168.1.2:3306;
    keepalive 32;
}

说明

  • 共享内存使所有 worker 进程共享 upstream 状态
  • 必需用于 least_connleast_time、健康检查等
  • 大小根据上游服务器数量调整

4. 核心指令

listen 指令

server {
    listen 80;                           # TCP
    listen 53 udp;                       # UDP
    listen 443 ssl;                      # TLS
    listen 12345 udp reuseport;          # UDP + reuseport
    listen [::]:80;                      # IPv6
    listen unix:/tmp/stream.sock;        # Unix Socket
}

参数说明

参数 说明
ssl 启用 SSL
udp UDP 协议
reuseport 每个 worker 独立监听
proxy_protocol 启用 PROXY 协议
backlog=N 连接队列长度
so_keepalive TCP keepalive
transparent 启用透明代理

proxy_pass 指令

server {
    listen 3306;
    proxy_pass 192.168.1.1:3306;
    proxy_pass backend;
    proxy_pass $upstream;
}

超时配置

server {
    proxy_timeout 10m;            # 客户端/后端之间读写超时(默认 10m
    proxy_connect_timeout 60s;    # 连接后端超时(默认 60s
}

缓冲配置

server {
    proxy_buffer_size 16k;        # 读写缓冲区大小(默认 16k
}

其他核心指令

指令 语法 默认值 上下文 说明
proxy_bind proxy_bind address [transparent]; stream, server 指定代理连接使用的本地地址
proxy_half_close proxy_half_close on | off; off stream, server 启用 TCP 半关闭支持
proxy_responses proxy_responses number; stream, server (UDP) UDP 每请求期望响应数
proxy_socket_keepalive proxy_socket_keepalive on | off; off stream, server 开启与上游的 TCP keepalive

示例配置

# 透明代理
server {
    listen 3306 transparent;
    proxy_bind $remote_addr transparent;
    proxy_pass backend:3306;
}

# UDP 配置
server {
    listen 53 udp;
    proxy_pass dns_backend:53;
    proxy_responses 1;  # 每请求期望1个响应
}

# 半关闭支持TCP 流式场景)
server {
    listen 9000;
    proxy_half_close on;
    proxy_pass backend:9000;
}

5. SSL/TLS 配置

服务端 SSL

stream {
    server {
        listen 443 ssl;
        ssl_certificate     /path/to/cert.pem;
        ssl_certificate_key /path/to/key.pem;
        ssl_protocols       TLSv1.2 TLSv1.3;

        proxy_pass backend:8080;
    }
}

上游 SSL连接后端使用 SSL

server {
    listen 3306;
    proxy_pass backend:3306;

    proxy_ssl on;
    proxy_ssl_protocols TLSv1.2 TLSv1.3;
    proxy_ssl_verify on;
    proxy_ssl_trusted_certificate /path/to/ca.pem;
    proxy_ssl_server_name on;
}

SSL 配置指令

指令 说明
ssl_certificate 证书文件
ssl_certificate_key 私钥文件
ssl_protocols 启用的协议
ssl_ciphers 加密套件
ssl_session_cache 会话缓存
proxy_ssl 启用上游 SSL
proxy_ssl_verify 验证上游证书
proxy_ssl_server_name 启用 SNI

完整上游 SSL 配置示例

server {
    listen 3306;
    proxy_pass ssl_backend:3306;

    proxy_ssl on;
    proxy_ssl_protocols TLSv1.2 TLSv1.3;
    proxy_ssl_ciphers HIGH:!aNULL;
    proxy_ssl_certificate /path/to/client.crt;
    proxy_ssl_certificate_key /path/to/client.key;
    proxy_ssl_verify on;
    proxy_ssl_trusted_certificate /path/to/ca.crt;
    proxy_ssl_verify_depth 2;
    proxy_ssl_name backend.example.com;
    proxy_ssl_session_reuse on;
}

指令说明

指令 说明
proxy_ssl_certificate 客户端证书mTLS
proxy_ssl_certificate_key 客户端证书私钥
proxy_ssl_ciphers 加密套件
proxy_ssl_verify_depth 证书链验证深度
proxy_ssl_name 验证上游证书的域名
proxy_ssl_session_reuse 启用 SSL 会话复用

6. 基于名称的虚拟服务器SNI

版本要求1.25.5+

stream {
    map $ssl_server_name $backend {
        app1.example.com app1_backend;
        app2.example.com app2_backend;
        default          default_backend;
    }

    upstream app1_backend {
        server 192.168.1.1:8080;
    }

    upstream app2_backend {
        server 192.168.1.2:8080;
    }

    server {
        listen 443 ssl;
        server_name app1.example.com app2.example.com;

        ssl_certificate     /path/to/cert.pem;
        ssl_certificate_key /path/to/key.pem;

        proxy_pass $backend;
    }
}

SSL Preread SNI 路由(无需终止 SSL

版本要求1.25.5+

使用 ssl_preread 模块在不解密的情况下读取 SNI 信息进行路由:

stream {
    ssl_preread on;  # 启用 ssl_preread
    
    map $ssl_preread_server_name $backend {
        mysql.example.com mysql_backend;
        redis.example.com redis_backend;
        default default_backend;
    }
    
    upstream mysql_backend {
        server 192.168.1.1:3306;
    }
    
    upstream redis_backend {
        server 192.168.1.2:6379;
    }
    
    upstream default_backend {
        server 192.168.1.3:8080;
    }
    
    server {
        listen 443;
        ssl_preread on;  # 在 server 上下文启用
        proxy_pass $backend;
    }
}

说明

  • ssl_preread 读取 ClientHello 中的 SNI 扩展
  • 无需配置 SSL 证书即可实现基于域名的路由
  • 适用于多服务共享端口的场景

7. PROXY 协议

接收 PROXY 协议

server {
    listen 3306 proxy_protocol;
    proxy_pass backend:3306;
}

发送 PROXY 协议

server {
    listen 3306;
    proxy_protocol on;
    proxy_pass backend:3306;
}

PROXY 协议变量

变量 说明
$proxy_protocol_addr 客户端 IP
$proxy_protocol_port 客户端端口
$proxy_protocol_server_addr 服务器 IP
$proxy_protocol_server_port 服务器端口

8. 速率限制

server {
    listen 3306;
    proxy_pass backend:3306;

    proxy_download_rate 1m;    # 限制从后端读取速率(字节/秒)
    proxy_upload_rate 1m;      # 限制从客户端读取速率(字节/秒)
}

使用变量动态限制

map $remote_addr $limit_rate {
    default     1m;
    10.0.0.1    10m;     # 特定 IP 更高速率
}

server {
    proxy_download_rate $limit_rate;
    proxy_upload_rate $limit_rate;
}

9. 故障转移

server {
    listen 3306;
    proxy_pass backend:3306;

    proxy_next_upstream on;              # 连接失败时尝试下一台
    proxy_next_upstream_timeout 30s;     # 总时间限制
    proxy_next_upstream_tries 3;         # 尝试次数限制
}

10. 连接保持

keepalive 连接池配置

upstream backend {
    server 192.168.1.1:3306;
    server 192.168.1.2:3306;
    
    keepalive 32;           # 空闲连接池大小
    keepalive_requests 100; # 单连接最大请求HTTP 适用)
    keepalive_timeout 60s;  # 空闲超时
}

指令说明

指令 语法 默认值 说明
keepalive keepalive connections; 保持到上游的空闲连接数
keepalive_requests keepalive_requests number; 100 单连接最大请求数
keepalive_timeout keepalive_timeout timeout; 60s 空闲连接超时时间

11. 内置变量

变量 说明
$remote_addr 客户端 IP
$remote_port 客户端端口
$server_addr 服务器 IP
$server_port 服务器端口
$protocol 协议TCP/UDP
$bytes_received 接收字节数
$bytes_sent 发送字节数
$session_time 会话时间(秒)
$status 会话状态
$ssl_preread_server_name SNI 名称

其他 stream 指令

指令 语法 默认值 说明
preread_buffer_size preread_buffer_size size; 16k 预读缓冲区大小
preread_timeout preread_timeout timeout; 30s 预读超时时间
resolver resolver address ... [valid=time]; DNS 解析器
resolver_timeout resolver_timeout time; 30s 解析超时
tcp_nodelay tcp_nodelay on | off; on 启用 TCP_NODELAY
variables_hash_max_size variables_hash_max_size size; 1024 变量哈希表最大大小
variables_hash_bucket_size variables_hash_bucket_size size; 64 变量哈希表桶大小

12. 应用场景示例

MySQL 代理

stream {
    upstream mysql_servers {
        least_conn;
        server mysql1:3306 weight=5;
        server mysql2:3306;
        server mysql3:3306 backup;
    }

    server {
        listen 3306;
        proxy_pass mysql_servers;
        proxy_timeout 600s;
        proxy_connect_timeout 2s;
    }
}

Redis 代理

stream {
    upstream redis_servers {
        server redis1:6379;
        server redis2:6379;
    }

    server {
        listen 6379;
        proxy_pass redis_servers;
        proxy_timeout 300s;
    }
}

DNS 代理

stream {
    upstream dns_servers {
        server 8.8.8.8:53;
        server 8.8.4.4:53;
    }

    server {
        listen 53 udp reuseport;
        proxy_pass dns_servers;
        proxy_timeout 20s;
        proxy_responses 1;   # 期望 1 个响应
    }
}

日志收集Syslog

stream {
    upstream syslog_servers {
        server syslog1:514;
        server syslog2:514;
    }

    server {
        listen 514 udp;
        proxy_pass syslog_servers;
        proxy_timeout 10s;
    }
}

WebSocket 代理TCP 层)

stream {
    upstream websocket_servers {
        server ws1:8080;
        server ws2:8080;
    }

    server {
        listen 8080;
        proxy_pass websocket_servers;
        proxy_timeout 3600s;  # 长连接超时
    }
}

13. 日志配置

stream {
    log_format main '$remote_addr [$time_local] '
                    '$protocol $status $bytes_sent $bytes_received '
                    '$session_time "$upstream_addr"';

    access_log /var/log/nginx/stream.log main;

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

14. 健康检查

被动检查(内置)

upstream backend {
    server 192.168.1.1:3306 max_fails=3 fail_timeout=30s;
    server 192.168.1.2:3306 max_fails=3 fail_timeout=30s;
}

主动检查NGINX Plus

upstream backend {
    zone backend 64k;

    server 192.168.1.1:3306;
    server 192.168.1.2:3306;

    health_check interval=5s passes=2 fails=3;
    health_check_timeout 5s;
}

15. 访问控制模块

ngx_stream_access_module

基于 IP 地址的访问控制,允许或拒绝特定客户端连接。

指令

指令 语法 默认值 上下文
allow allow address | CIDR | unix: | all; stream, server
deny deny address | CIDR | unix: | all; stream, server

配置示例

stream {
    # 数据库访问控制
    server {
        listen 3306;
        
        # 允许内网访问
        allow 10.0.0.0/8;
        allow 192.168.0.0/16;
        allow 172.16.0.0/12;
        
        # 拒绝其他所有
        deny all;
        
        proxy_pass mysql_backend;
    }

    # Redis 访问控制
    server {
        listen 6379;
        
        # 仅允许特定 IP
        allow 192.168.1.100;
        allow 192.168.1.101;
        deny all;
        
        proxy_pass redis_backend;
    }

    # 管理端口(仅本地)
    server {
        listen 9000;
        
        allow 127.0.0.1;
        deny all;
        
        proxy_pass admin_backend;
    }
}

规则匹配顺序

  • 按配置顺序依次检查
  • 首个匹配的规则决定结果
  • 未匹配任何规则时默认允许

16. 连接限制模块

ngx_stream_limit_conn_module

限制并发连接数,防止资源耗尽。

指令

指令 语法 默认值 上下文
limit_conn_zone limit_conn_zone key zone=name:size; stream
limit_conn limit_conn zone number; stream, server
limit_conn_log_level limit_conn_log_level info | notice | warn | error; error stream, server

配置示例

stream {
    # 按客户端 IP 限制连接数
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    
    # 按上游服务器限制连接数
    limit_conn_zone $server_addr zone=server:10m;

    # MySQL 代理 - 每 IP 最多 10 个连接
    server {
        listen 3306;
        limit_conn addr 10;
        proxy_pass mysql_backend;
    }

    # Redis 代理 - 每 IP 最多 5 个连接
    server {
        listen 6379;
        limit_conn addr 5;
        limit_conn_log_level warn;
        proxy_pass redis_backend;
    }

    # 全局连接限制
    server {
        listen 8080;
        limit_conn addr 50;      # 每 IP 最多 50
        limit_conn server 1000;  # 服务总连接上限
        proxy_pass backend;
    }
}

内存计算

  • 1MB 共享内存可存储约 16,000 个 32 字节 key$binary_remote_addr
  • 或约 8,000 个 IPv6 地址16 字节)

17. 地理位置模块

ngx_stream_geo_module

根据客户端 IP 地址创建变量值,用于地理路由或访问控制。

指令

指令 语法 默认值 上下文
geo geo [$address] $variable { ... } stream

配置示例

stream {
    # 基础地理映射
    geo $remote_addr $region {
        default        other;
        10.0.0.0/8     internal;
        192.168.0.0/16 internal;
        172.16.0.0/12  internal;
        
        # 中国大陆 IP 段(示例)
        1.0.1.0/24     china;
        1.0.2.0/23     china;
        # ... 更多 IP 段
    }

    # 使用变量进行路由
    map $region $backend_pool {
        internal  internal_backend;
        china     china_backend;
        other     global_backend;
    }

    upstream internal_backend {
        server 192.168.1.1:3306;
    }

    upstream china_backend {
        server 10.0.1.1:3306;
    }

    upstream global_backend {
        server 10.0.2.1:3306;
    }

    server {
        listen 3306;
        proxy_pass $backend_pool;
    }
}

高级用法

stream {
    # 使用变量作为地址源
    geo $realip_remote_addr $region {
        default other;
        # ... 配置
    }

    # 带 CIDR 包含
    geo $country {
        default    XX;
        include    /etc/nginx/geo/countries.conf;
    }

    # countries.conf 内容示例:
    # 1.0.1.0/24     CN;
    # 1.0.2.0/23     CN;
    # 1.1.1.0/24     AU;
}

    # 使用 GeoIP 数据库(需 ngx_stream_geoip_module
    geoip_country /usr/share/GeoIP/GeoIP.dat;
    
    map $geoip_country_code $backend {
        default global_backend;
        CN      china_backend;
        US      us_backend;
        EU      eu_backend;
    }
}

18. 真实 IP 模块

ngx_stream_realip_module

处理 PROXY 协议头,获取客户端真实 IP 地址。

指令

指令 语法 默认值 上下文
set_real_ip_from set_real_ip_from address | CIDR; stream, server
real_ip_header real_ip_header field; proxy_protocol stream, server

配置示例

stream {
    server {
        listen 3306 proxy_protocol;  # 接收 PROXY 协议
        
        # 信任的代理服务器地址
        set_real_ip_from 10.0.0.0/8;
        set_real_ip_from 192.168.0.0/16;
        set_real_ip_from 172.16.0.0/12;
        
        proxy_pass mysql_backend;
    }
}

可用变量

变量 说明
$realip_remote_addr 原始客户端地址PROXY 协议中的地址)
$realip_remote_port 原始客户端端口
$proxy_protocol_addr PROXY 协议中的客户端地址
$proxy_protocol_port PROXY 协议中的客户端端口
$proxy_protocol_server_addr PROXY 协议中的目标服务器地址
$proxy_protocol_server_port PROXY 协议中的目标服务器端口

典型场景

stream {
    # 场景:负载均衡器 → NGINX → 后端
    server {
        listen 3306 proxy_protocol;
        
        # 负载均衡器的 IP
        set_real_ip_from 10.0.0.1;
        set_real_ip_from 10.0.0.2;
        
        # 使用真实 IP 进行限流
        limit_conn_zone $realip_remote_addr zone=conn_limit:10m;
        limit_conn conn_limit 10;
        
        # 日志记录真实 IP
        log_format main '$realip_remote_addr [$time_local] '
                        '$protocol $status $bytes_sent $bytes_received';
        access_log /var/log/nginx/stream.log main;
        
        proxy_pass mysql_backend;
    }
}

19. 高级日志配置

日志格式详解

stream {
    # JSON 格式日志
    log_format json_combined escape=json
        '{'
            '"time_local":"$time_local",'
            '"remote_addr":"$remote_addr",'
            '"server_addr":"$server_addr",'
            '"server_port":"$server_port",'
            '"protocol":"$protocol",'
            '"status":"$status",'
            '"bytes_sent":"$bytes_sent",'
            '"bytes_received":"$bytes_received",'
            '"session_time":"$session_time",'
            '"upstream_addr":"$upstream_addr",'
            '"upstream_bytes_sent":"$upstream_bytes_sent",'
            '"upstream_bytes_received":"$upstream_bytes_received",'
            '"upstream_connect_time":"$upstream_connect_time"'
        '}';

    # 详细格式日志
    log_format detailed '$remote_addr - [$time_local] '
                        '$protocol/$status '
                        'sent:$bytes_sent recv:$bytes_received '
                        'time:$session_time '
                        'upstream:$upstream_addr '
                        'upstream_time:$upstream_connect_time';

    # 条件日志(仅记录错误)
    map $status $loggable {
        ~^[23]  0;
        default 1;
    }

    server {
        listen 3306;
        access_log /var/log/nginx/stream.json json_combined;
        access_log /var/log/nginx/stream_errors.log detailed if=$loggable;
        proxy_pass backend;
    }
}

日志缓冲与压缩

stream {
    server {
        listen 3306;
        
        # 缓冲写入(提升性能)
        access_log /var/log/nginx/stream.log main buffer=32k flush=5s;
        
        # gzip 压缩日志
        access_log /var/log/nginx/stream.log.gz main gzip buffer=32k;
        
        proxy_pass backend;
    }
}

open_log_file_cache

缓存日志文件描述符,减少文件打开操作:

stream {
    open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;
    
    server {
        listen 3306;
        access_log /var/log/nginx/stream.log main;
        proxy_pass backend;
    }
}

参数说明

参数 说明
max 缓存的最大文件描述符数
inactive 非活动文件保留时间
valid 检查文件是否有效的时间间隔
min_uses 最小使用次数才缓存