1. 为什么需要Nginx Stream转发MySQL连接
很多开发团队都会遇到这样的场景:业务系统部署在公有云上,但数据库服务器放在内网环境。直接暴露MySQL的3306端口显然不安全,黑客扫描到开放端口后可能尝试暴力破解。我见过太多因为数据库端口暴露导致的安全事件,轻则数据泄露,重则整个数据库被勒索加密。
这时候Nginx的Stream模块就像个智能门卫,它能在公网和内网之间建立加密通道。具体来说有三大优势:
- 隐藏真实数据库信息:外部客户端只能看到Nginx的IP和端口,完全不知道后端MySQL服务器的存在
- 流量控制:可以设置连接超时、最大连接数等参数,避免数据库被突发流量打垮
- 审计日志:Nginx能记录所有访问记录,方便后续安全审计
去年我们有个电商项目就用了这个方案。当时客户要求供应商能实时查询库存数据,但又不愿意直接开放数据库访问。用Nginx转发后,供应商通过特定端口连接,我们在Nginx层还做了IP白名单限制,既满足了业务需求又保障了安全。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与模块检查
2.1 确认Nginx版本
在开始配置前,先确认你的Nginx是否支持stream模块。执行这个命令查看编译参数:
bash复制nginx -V 2>&1 | grep -o with-stream
如果输出with-stream就说明已经支持。这里有个坑要注意:用yum/apt安装的Nginx通常自带stream模块,但源码编译安装的可能需要手动添加。我去年帮客户排查问题时就遇到过,他们自己编译的Nginx死活不生效,最后发现是漏了--with-stream参数。
2.2 源码安装补充模块
如果确实缺少stream模块,需要重新编译。分享下我的标准编译参数:
bash复制./configure \
--prefix=/usr/local/nginx \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_gzip_static_module \
--with-stream \
--with-stream_ssl_module
关键点在于最后两个参数:--with-stream启用基础模块,--with-stream_ssl_module支持SSL加密。编译完成后别急着重启,先用nginx -t测试配置文件,避免服务崩溃。
3. 核心配置详解
3.1 基础转发配置
在nginx.conf的最外层(注意不是在http块内)添加stream配置:
nginx复制stream {
upstream mysql_backend {
server 192.168.1.100:3306 weight=1;
}
server {
listen 3307;
proxy_pass mysql_backend;
proxy_connect_timeout 3s;
proxy_timeout 1h;
}
}
这个配置实现了:
- 监听公网3307端口
- 将请求转发到内网192.168.1.100的3306端口
- 连接超时设为3秒(避免长时间等待)
- 会话超时设为1小时(适合长时间查询)
3.2 高级安全配置
实际生产环境我建议增加这些参数:
nginx复制server {
listen 3307 so_keepalive=on;
proxy_pass mysql_backend;
proxy_buffer_size 16k;
# 连接控制
proxy_connect_timeout 10s;
proxy_timeout 2h;
# 流量限制
proxy_upload_rate 512k;
proxy_download_rate 2m;
# 错误处理
proxy_next_upstream on;
proxy_next_upstream_timeout 5s;
proxy_next_upstream_tries 3;
}
特别是proxy_upload_rate和proxy_download_rate这两个参数,能防止某个客户端耗尽所有带宽。曾经有个报表系统因为没有限速,一个全表查询把网络带宽占满了。
4. 权限与SELinux问题处理
4.1 端口权限问题
第一次启动时可能会遇到这个错误:
code复制bind() to 0.0.0.0:3307 failed (13: Permission denied)
这是因为Linux系统默认不允许非root进程监听1024以下端口。有三种解决方案:
- 使用1024以上端口(比如53307)
- 给Nginx二进制文件赋予CAP_NET_BIND_SERVICE能力:
bash复制setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx - 用iptables做端口转发:
bash复制
iptables -t nat -A PREROUTING -p tcp --dport 3307 -j REDIRECT --to-port 53307
4.2 SELinux策略调整
如果系统启用了SELinux(用getenforce命令查看),需要执行:
bash复制semanage port -a -t http_port_t -p tcp 3307
这比直接关闭SELinux更安全。我总跟客户强调:安全机制不是我们的敌人,要学会和它们共处。
5. 连接测试与排错
5.1 基础连通性测试
先用telnet检查端口是否开放:
bash复制telnet your_nginx_ip 3307
如果连接立即断开,可能是:
- Nginx配置未生效 - 执行
nginx -s reload - 防火墙未放行 - 检查iptables/ufw规则
- 上游MySQL服务未启动 - 登录内网检查MySQL状态
5.2 实际数据库连接测试
推荐用mysql客户端实测:
bash复制mysql -h nginx_host -P 3307 -u dbuser -p
如果连接成功但查询报错,可能是:
- MySQL用户权限不足(需要允许从NginxIP连接)
- 字符集不匹配(在Nginx配置中添加
proxy_set_header)
6. 性能优化实战经验
经过多个项目实践,我总结了这些优化技巧:
-
连接池配置:
nginx复制upstream mysql_backend { server 192.168.1.100:3306 max_conns=100; keepalive 20; }max_conns限制最大连接数,keepalive维持长连接 -
TCP参数调优:
nginx复制server { listen 3307 so_keepalive=30m::10; tcp_nodelay on; }这个配置会:
- 启用TCP keepalive(30分钟空闲后探测)
- 10秒内探测3次失败才断开
- 禁用Nagle算法(降低查询延迟)
-
监控指标:
在Nginx状态模块中添加:nginx复制server { listen 8080; location /stream_status { stream_status; } }这样可以实时查看活跃连接数、流量等指标
7. 安全加固方案
7.1 IP白名单控制
只允许特定IP访问:
nginx复制server {
listen 3307;
allow 203.0.113.5;
allow 198.51.100.0/24;
deny all;
proxy_pass mysql_backend;
}
7.2 SSL加密传输
生成证书后配置:
nginx复制server {
listen 3307 ssl;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
proxy_pass mysql_backend;
}
7.3 限流保护
防止暴力破解:
nginx复制limit_conn_zone $binary_remote_addr zone=mysql_conn:10m;
server {
listen 3307;
limit_conn mysql_conn 5;
proxy_pass mysql_backend;
}
每个IP最多允许5个并发连接
8. 常见问题解决方案
问题1:连接经常超时断开
- 检查
proxy_timeout值是否过小 - 网络是否存在丢包(用ping和tcpdump排查)
- MySQL的
wait_timeout参数是否小于Nginx的超时设置
问题2:大数据量查询失败
- 增加
proxy_buffer_size(建议16k-64k) - 调整
proxy_upload_rate和proxy_download_rate - 检查MySQL的
max_allowed_packet参数
问题3:性能瓶颈定位
- 用
ss -tlnp查看连接状态 - 通过Nginx日志分析响应时间
- 对比直接连接MySQL和通过Nginx连接的性能差异
记得有次客户反映查询变慢,最后发现是Nginx的buffer设置太小,导致大量小包传输。调整后性能提升了40%。
