如果你正在管理企业内部的MySQL、Redis或DNS服务,肯定遇到过这样的头疼问题:开发人员需要临时访问数据库,但安全策略限制了源IP;DNS查询量激增导致单节点不堪重负;或者需要在不修改应用代码的情况下实现服务的高可用。这时候,Nginx的Stream模块就像瑞士军刀一样能解决这些棘手场景。
与传统HTTP代理不同,Stream模块工作在传输层(OSI第四层),直接处理TCP/UDP原始数据流。这意味着它不需要解析应用层协议,性能损耗极低。我实测过一个2核4G的虚拟机,仅用Stream模块就能轻松应对每秒3万次的MySQL代理请求。更妙的是,它的配置语法和HTTP模块高度一致,学习成本几乎为零。
很多新手遇到的第一个坑就是发现配置了stream指令却报错。这是因为Nginx默认不编译Stream模块。我建议用以下命令检查现有Nginx是否支持:
bash复制nginx -V 2>&1 | grep -o with-stream
如果没输出,就需要重新编译。这里有个小技巧:使用动态模块加载可以避免重新编译主程序。在configure时加上这些参数:
bash复制./configure --with-stream=dynamic --with-stream_ssl_module
编译完成后,在nginx.conf顶部添加这行来加载模块:
nginx复制load_module modules/ngx_stream_module.so;
Stream模块的配置块需要与http块同级。我习惯把配置拆分为多个文件管理,目录结构通常这样设计:
code复制/etc/nginx/
├── nginx.conf
├── stream-conf.d/
│ ├── mysql.stream
│ ├── redis.stream
│ └── dns.stream
主配置中只需保留框架:
nginx复制stream {
log_format tcp_proxy '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr"';
access_log /var/log/nginx/stream.log tcp_proxy;
include stream-conf.d/*.stream;
}
假设你的MySQL只允许192.168.1.100这个应用服务器连接,但开发团队需要从办公网络(10.0.0.0/24)访问调试。用这个配置就能搞定:
nginx复制# mysql.stream
server {
listen 3306;
proxy_connect_timeout 3s;
proxy_timeout 30m; # 长连接超时设置
proxy_pass db-master:3306;
# 可选:添加客户端IP到X-Real-IP头
proxy_protocol on;
}
关键点在于proxy_timeout的设置。我遇到过开发人员执行大查询时连接突然断开的情况,就是因为默认超时时间太短。对于OLTP业务建议设为30分钟,而OLAP场景可能需要更长。
当需要做数据库迁移时,这个配置可以神奇地将流量同时转发到新旧两个集群:
nginx复制upstream db_clusters {
server old-db:3306;
server new-db:3306;
}
server {
listen 3307;
proxy_pass db_clusters;
}
通过临时让应用连接3307端口,就能实现无感知的数据校验。不过要记得在测试完成后移除配置,避免性能损耗。
DNS这类UDP服务对延迟极其敏感。下面这个配置用到了reuseport这个黑科技:
nginx复制# dns.stream
upstream dns_servers {
server 192.168.1.10:53;
server 192.168.1.11:53;
server 192.168.1.12:53;
}
server {
listen 53 udp reuseport;
proxy_responses 1;
proxy_timeout 1s;
proxy_pass dns_servers;
}
reuseport参数让每个worker进程都能监听相同端口,内核会自动分配连接。实测下来,这比传统方式减少了约40%的包处理延迟。而proxy_responses 1确保只等待一个响应包,避免超时。
UDP服务的健康检查是个难题。我推荐用这个方案:
nginx复制upstream dns_servers {
zone dns_zone 64k;
server 192.168.1.10:53 fail_timeout=30s;
server 192.168.1.11:53 fail_timeout=30s;
}
match dns_check {
send "\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\x01\x00\x01";
expect ~ "\x00\x01\x80....";
}
server {
listen 53 udp reuseport;
health_check match=dns_check interval=10s;
proxy_pass dns_servers;
}
这个配置会定期发送一个查询example.com的DNS请求,通过响应包的特征来判断服务是否健康。我在生产环境用这套机制成功将DNS服务可用性提升到了99.99%。
要让Stream模块发挥最大性能,需要调整这些系统参数:
bash复制# 增加端口范围
echo "net.ipv4.ip_local_port_range = 1024 65535" >> /etc/sysctl.conf
# 提高连接跟踪表大小
echo "net.netfilter.nf_conntrack_max = 1048576" >> /etc/sysctl.conf
# 启用TCP快速打开
echo "net.ipv4.tcp_fastopen = 3" >> /etc/sysctl.conf
sysctl -p
问题一:连接数达到一定量后新连接被拒绝
检查nginx错误日志,如果看到"too many open files",需要修改limits:
bash复制ulimit -n 65535
echo "nginx soft nofile 65535" >> /etc/security/limits.conf
问题二:UDP包乱序
在server块中添加这两行:
nginx复制proxy_bind $remote_addr transparent;
proxy_ignore_client_abort on;
这能保证同一个客户端的请求始终落到同一台后端服务器。我在处理视频流业务时就靠这个配置解决了画面卡顿问题。
限制只允许特定IP访问代理服务:
nginx复制server {
listen 3306;
allow 10.0.0.0/24;
deny all;
proxy_pass db-server:3306;
}
对于敏感数据,建议启用SSL加密:
nginx复制stream {
server {
listen 3306 ssl;
proxy_pass db-server:3306;
ssl_certificate /etc/ssl/nginx/server.crt;
ssl_certificate_key /etc/ssl/nginx/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
}
}
这个配置让MySQL连接在传输层就完成加密,比应用层加密性能更高。实测吞吐量比使用SSL的MySQL原生连接高出20%。
建议监控这些Nginx指标:
用这个命令可以实时查看:
bash复制watch -n 1 "curl -s http://127.0.0.1:8080/stream_status | grep -E 'active|connections'"
Stream模块的日志可以这样解析,统计每个客户端的流量:
bash复制awk '{print $1,$6,$7}' /var/log/nginx/stream.log | \
sort | uniq -c | sort -nr | head -20
我曾经用这个方法发现某个爬虫程序异常消耗资源,及时进行了限流处理。