NGINX `ngx_stream_core_module` 模块概览
一、模块定位与功能
-
通用 TCP/UDP 代理
- 支持同时处理 TCP 和 UDP 流量,透明转发请求到后端服务器组(upstream)。
- 可作为四层负载均衡,根据客户端 IP、权重、最少连接等策略将连接分发给后端。
-
预读(preread)机制
- 在代理决策前预先读取部分客户端数据(如协议握手头),可用于后端选择或日志统计。
-
动态 DNS 解析
resolver
指令允许在运行时解析后端主机名,适用于后端节点 IP 可能变动的场景。
-
高级监听配置
listen
指令支持多种参数(udp
/proxy_protocol
/reuseport
/so_keepalive
等),可细粒度控制监听 socket 特性。
-
内置 Hash、变量机制
- 可通过
$remote_addr
等内置变量实现基于客户端 IP 的一致性哈希、最少连接等负载均衡方式。 variables_hash_bucket_size
、variables_hash_max_size
用于调优内部变量哈希表。
- 可通过
二、关键指令详解
1. stream { … }
位于主配置最顶层,用于开启“流”模块上下文,所有流相关配置都置于其内:
stream {…
}
2. upstream <name> { … }
定义一组后端服务器,用于负载均衡:
stream {upstream backend {hash $remote_addr consistent;server backend1.example.com:12345 weight=5;server 192.168.0.2:12345 max_fails=3 fail_timeout=30s;server unix:/tmp/backend.sock;}…
}
hash <key> [consistent]
:基于 key(如$remote_addr
)做哈希,一致性模式下后端增删时扰动最小。weight
、max_fails
、fail_timeout
:与 HTTP upstream 相同,用于调整权重与健康检查策略。
3. server { … }
在 stream
块内定义一个虚拟服务器,监听指定端口并将流量转发到某个 upstream 或单个后端:
stream {server {listen 12345; # 监听 TCP 端口 12345proxy_connect_timeout 1s; # 连接后端超时proxy_timeout 3s; # 读写后端超时proxy_pass backend; # 转发到 upstream backend}server {listen 127.0.0.1:53 udp reuseport; # 监听本地 UDP 53proxy_timeout 20s;proxy_pass dns_upstream; # 转发到 upstream dns_upstream}server {listen [::1]:12345; # IPv6 地址监听proxy_pass unix:/tmp/stream.sock; # 转发到 Unix 域套接字}
}
listen <address:port> [参数...]
:配置监听地址和端口,可附加udp
、ssl
(若启用 TLS)、proxy_protocol
、reuseport
等。proxy_pass <upstream|address>
:将流量转发到指定 upstream 名称或后端地址(支持 Unix Socket)。proxy_connect_timeout
:与后端建立连接的最长期限。proxy_timeout
:包括连接、读、写操作的总体超时,若在此期间无数据交换则断开。
常用 listen
参数
udp
:将该监听 socket 切换为 UDP 模式(默认 TCP)。proxy_protocol
:启用 PROXY 协议解码,用于获取客户端真实 IP(需后端或网络层支持 PROXY 协议)。reuseport
:为每个工作进程分别创建监听 socket,实现内核级负载均衡。so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]
:细粒度控制 TCP Keepalive 行为。fastopen=number
:启用 TCP Fast Open,需谨慎使用(允许跨握手发送少量数据)。bind
:强制对每个监听地址单独执行bind()
,以便与高级参数兼容。
4. preread_buffer_size <size>
默认:
16k
上下文:stream, server
在真正将客户端流量代理给后端之前,预先在 NGINX 层读取的数据量,用于协议判断、日志或统计。
server {listen 54321;preread_buffer_size 32k; # 预读 32KBproxy_pass backend;
}
若需要从预读数据中提取特征(如协议头部),可相应调大此缓冲。
5. preread_timeout <time>
默认:
30s
上下文:stream, server
预读阶段超时时间,若在此期间客户端无数据发送,则关闭连接。
server {listen 54321;preread_timeout 5s; # 5 秒未接收数据则关闭proxy_pass backend;
}
6. proxy_protocol_timeout <time>
默认:
30s
上下文:stream, server
当在 listen … proxy_protocol
模式下启用 PROXY 协议,此指令规定在读取完整 PROXY 协议头前的最大等待时间:
server {listen 12345 proxy_protocol;proxy_protocol_timeout 2s; # 2 秒读完 PROXY 协议头proxy_pass backend;
}
如果客户端连接后未在 proxy_protocol_timeout
指定时间内发送完整的 PROXY 头,则会被关闭。
7. resolver <address> […] [valid=time] [ipv4=on|off] [ipv6=on|off] [status_zone=zone]
默认:无
上下文:stream, server
为后端 upstream 名称解析提供 DNS 服务器列表:
stream {resolver 127.0.0.1 [::1]:5353 valid=30s;resolver_timeout 5s;upstream dynamic_backend {server backend1.example.com:12345 resolve;server backend2.example.com:12345 resolve;}server {listen 12345;proxy_pass dynamic_backend;}
}
valid=30s
:覆盖 DNS TTL,强制每隔 30 秒刷新缓存。ipv4=off
/ipv6=off
:控制是否解析 IPv4/IPv6。resolver_timeout
:DNS 查询超时时间。
8. tcp_nodelay on|off
默认:
on
上下文:stream, server
开启或关闭 TCP_NODELAY, 用于禁用 Nagle 算法,减少小包延迟:
stream {tcp_nodelay on;server {listen 10000;proxy_pass backend;}
}
多数场景下推荐开启(on
),尤其对低延迟要求较高的协议(如 Redis、Memcached 等)。
9. 哈希表调优:variables_hash_bucket_size
/ variables_hash_max_size
默认:
64
/1024
上下文:stream
- 用途:当你在 Stream 配置中使用自定义变量或正则表达式时,会在内部构建哈希表。通过这两项指令可手动调整哈希桶大小及最大容量,避免“桶冲突”或占用过多内存。
stream {variables_hash_bucket_size 128;variables_hash_max_size 2048;server {listen 11000;set $client_ip $remote_addr; # 使用自定义变量proxy_pass "$client_ip:20000";}
}
10. server_name
、server_names_hash_bucket_size
、server_names_hash_max_size
上下文:
stream, server
(自 1.25.5 起支持 Server Name Matching)
- 仅在需要基于 TLS SNI(Server Name Indication)或IP+端口 匹配多个虚拟服务器时使用。
server_name
声明可匹配的主机名(支持通配符、正则);- 对应的哈希表调优参数与上文类似,用于存储大量 server_name 时的性能优化。
stream {variables_hash_bucket_size 64;variables_hash_max_size 1024;server {listen 443 ssl;server_name example.com www.example.org;ssl_certificate /etc/nginx/ssl/stream.pem;ssl_certificate_key /etc/nginx/ssl/stream.key;proxy_pass backend;}
}
三、常用内置变量
在 stream
配置与日志中,可使用以下常见变量:
-
客户端信息
$remote_addr
:客户端 IP;$remote_port
:客户端端口;$binary_remote_addr
:二进制形式的客户端 IP,适合做哈希。
-
代理协议(需
proxy_protocol
)$proxy_protocol_addr
/$proxy_protocol_port
:来自 PROXY 头中的真实 IP/端口;$proxy_protocol_server_addr
/$proxy_protocol_server_port
:服务器地址/端口(1.17.6+);$proxy_protocol_tlv_*
:支持访问 PROXY Protocol v2 TLV(如$proxy_protocol_tlv_ssl_version
)。
-
会话统计
$bytes_received
/$bytes_sent
:当前会话已接收/发送字节数(1.11.4+);$session_time
:会话时长(秒.msec)(1.11.4+);$status
:当前会话状态码(200、400、403、500、502、503)(1.11.4+);
-
服务器信息
$server_addr
/$server_port
:接收连接的本地地址及端口;$hostname
:Nginx 主机名;$pid
:工作进程 PID;
-
时间
$msec
:当前时间(秒.毫秒);$time_local
:本地时间(Common Log 格式);$time_iso8601
:本地时间(ISO 8601 格式);
-
其他
$nginx_version
:Nginx 编译版本;$connection
:会话编号(对当前 worker 进程)。
示例日志格式:
stream {log_format stream_log '$remote_addr:$remote_port ''$proxy_protocol_addr:$proxy_protocol_port - ''bytes_in=$bytes_received bytes_out=$bytes_sent ''status=$status ''time=$msec';access_log /var/log/nginx/stream_access.log stream_log;server {listen 3306; # MySQL Proxyproxy_pass mysql_upstream;}
}
四、完整示例
以下示例演示如何基于 ngx_stream_core_module
搭建一个通用的 TCP 流代理环境,支持多个后端集群、动态 DNS、PROXY 协议以及日志统计。
worker_processes auto;error_log /var/log/nginx/error.log info;events {worker_connections 2048;
}stream {################################# 全局调优(哈希、缓冲) #################################preread_buffer_size 16k;preread_timeout 10s;proxy_protocol_timeout 5s;tcp_nodelay on;variables_hash_bucket_size 64;variables_hash_max_size 2048;################################# DNS 动态解析与超时 #################################resolver 127.0.0.1 [::1]:5353 valid=30s ipv6=on ipv4=on;resolver_timeout 3s;################################# 定义后端 upstream 集群 #################################upstream mysql_backend {hash $binary_remote_addr consistent; server db1.example.com:3306 weight=5;server db2.example.com:3306 weight=5;server 192.168.1.100:3306 max_fails=2 fail_timeout=20s;}upstream redis_backend {server 10.0.0.10:6379;server redis2.example.com:6379 resolve; # 动态 DNS}upstream dns_servers {server 192.168.0.53:53;server dns2.example.com:53 resolve;}################################# MySQL TCP 代理 (3306) #################################server {listen 3306;proxy_connect_timeout 1s;proxy_timeout 5s;proxy_pass mysql_backend;# 记录访问日志access_log /var/log/nginx/stream_mysql.log '$remote_addr:$remote_port ''bytes_in=$bytes_received bytes_out=$bytes_sent ''time=$msec status=$status';}################################# Redis TCP 代理 (6379) #################################server {listen 6379;proxy_connect_timeout 0.5s;proxy_timeout 2s;proxy_pass redis_backend;access_log /var/log/nginx/stream_redis.log '$remote_addr:$remote_port -> $upstream_addr ''rx=$bytes_received tx=$bytes_sent time=$msec';}################################# DNS UDP 代理 (53) #################################server {listen 53 udp reuseport;proxy_pass dns_servers;proxy_timeout 10s;access_log /var/log/nginx/stream_dns.log '$remote_addr:$remote_port ''dns_request rx=$bytes_received tx=$bytes_sent ''time=$msec';}################################# Unix Domain Socket 代理示例 ## ###############################server {listen unix:/var/run/stream.sock;proxy_pass unix:/var/run/backend.sock;}
}
要点解析:
- 多协议示例:TCP(MySQL、Redis)和 UDP(DNS)共存,示范了
listen … udp reuseport;
与常规 TCP 监听的区别。 - 动态 DNS:后端
redis_backend
和dns_servers
使用resolve
,结合全局resolver
定期刷新。 - 健康检查参数:
max_fails=2 fail_timeout=20s
,后端db1
连续失败 2 次且在 20 秒内失败则被标记为不可用。 - 日志记录:示例自定义了访问日志格式,记录客户端 IP/端口、上下行字节数、会话时长与状态码。
- UDP 重用端口:
reuseport
让内核将 53 端口请求分发给多个 worker 进程,提高并发 DNS 性能。 - Unix 域套接字:可将流量从一个 Unix Socket 转发到另一个,适用于本机进程间高性能通信。
五、总结
ngx_stream_core_module
是 NGINX 在第四层(L4)协议上的通用解决方案,能够:
- 支持任意 TCP/UDP 转发与负载均衡;
- 提供预读、超时、PROXY 协议、安全套接字等高级功能;
- 允许动态 DNS 解析、哈希负载均衡、TCP 优化(TCP_NODELAY、Keepalive);
- 与内置变量结合,轻松实现日志统计与会话追踪。
通过合理配置上述指令,你可以根据不同业务场景(MySQL、Redis、DNS、SMTP 等)构建高可用、高并发且安全可靠的四层代理架构。希望本文的指令详解与示例配置,能让你快速掌握 ngx_stream_core_module
并在生产环境中灵活运用。