3.3 四层Stream代理与全链路演练


3.3 四层Stream代理与全链路演练

本节摘要:stream 模块让 Nginx 在 TCP/UDP 层做代理与负载均衡,不解析 HTTP,开销更低、适用更广——数据库、Redis、消息队列的入口都能管。本节配置 MySQL 读写分离入口,并用一次全链路压测给第三章的扩容画上句号。

七层之外的世界

http 模块工作在应用层:读完整 HTTP 请求、改写头部、按 URI 调度。stream 模块工作在传输层:只见字节流,不管内容是什么。代价是没有 location、不能改 HTTP 头;收益是通用与轻量。

维度 七层 http 代理 四层 stream 代理
调度依据 URI、头部、Host 仅 IP 与端口
内容改写 支持 不支持
协议适配 HTTP 系 任意 TCP/UDP
适用 Web 应用 数据库、缓存、MQ、TLS 直通

给 MySQL 配一个读写分离入口

stream { upstream mysql_write { server 10.0.2.21:3306; # 主库 } upstream mysql_read { least_conn; server 10.0.2.22:3306; # 从库1 server 10.0.2.23:3306; # 从库2 } server { listen 13306; proxy_pass mysql_write; proxy_connect_timeout 3s; } server { listen 13307; proxy_pass mysql_read; proxy_timeout 600s; # 匹配慢查询长连接 } }

应用侧写操作连 13306、读操作连 13307,主从切换时只改 Nginx 配置,应用零改动。注意 stream 块与 http 块平级,都写在最外层;proxy_timeout 要按数据库长连接的实际情况放宽,默认 10 分钟对夜间批量任务不够。

⚠️ stream 监听的后端最好同时限制来源 IP:allow/deny 只信任应用网段,否则等于把数据库端口暴露给了所有能连到 Nginx 的客户端。

server { listen 13306; allow 10.0.1.0/24; # 应用网段 deny all; proxy_pass mysql_write; }

全链路压测验收

扩容完成后,用 wrk 从外部打真实页面接口,验证整条链路:

wrk -t8 -c2000 -d60s --latency https://shop.example.com/api/items

关注三个数字的相对关系:

Latency 分布 :P99 是用户体验的真相,平均值会撒谎 upstream_response_time 日志:后端耗时占比,判断是否还需要扩应用 错误率 :0% 是验收线,出现 502 先查 2.3 节的缓冲与超时

图:扩容后的全链路结构

图:扩容后的全链路结构

stream 模块的启用与排错

stream 是独立模块,仓库版默认不编译进主二进制。装包后若报"unknown directive stream",先确认模块文件在不在:

# Debian/Ubuntu:把 stream 模块的 include 放进主配置顶部 ls /usr/share/nginx/modules/ | grep stream # 主配置最外层加一行(或在 conf.modules.d 里已自带) # load_module /usr/lib/nginx/modules/ngx_stream_module.so; nginx -t && nginx -s reload

第二类常见报错是 stream 与 http 里的 upstream 重名——两个模块的命名空间是合并的,mysql_read 若在 http 里也有定义,启动直接冲突。命名规范上给四层 upstream 统一加前缀(如 st_mysql_read),一类问题就此绝迹。

主从切换演练

stream 入口统一后的最大红利是切换成本趋近于零。演练流程:应用零改动,运维只改一行 server 地址后 reload,观察切换窗口内的连接行为:

T0 改配置:10.0.2.21:3306 → 10.0.2.24:3306(新主库) T0+reload 新连接全部落到新主库 T0+5s 存量长连接仍在旧主库上(proxy_timeout 内不掐断) T0+60s 配合应用侧连接池回收,旧连接自然清零

演练揭示的行为要点:reload 不主动掐断 stream 的存量连接,切换不是瞬时的——应用连接池的最大生命周期要设成小于可容忍的切换窗口,否则会有应用长时间连在旧库上读写。这个参数的配合是四层代理方案里最容易被遗漏的一环。

四层方案的监控也常被遗漏:stream 的连接不产生 access_log 里的 HTTP 记录,需要单独打开 stream 日志才能看见谁在连、连了多少。日志格式沿用 Nginx 的 log_format 机制,关键字段是远程地址、目标 upstream 与连接时长。没有这层可见性,数据库入口的异常连接(应用连接池泄漏、扫描器试探)完全不可见——等数据库连接数告警时,问题已经在下游积压了很久。四层入口的可见性建设,应当与代理本身同一天上线。

四层代理还有一个不常见但救过场的用法:协议不明的神秘流量分析。某次安全团队怀疑内网某端口被滥用,把 Nginx stream 挂在该端口做透明中转并全量记录连接对,一天就画出了完整的访问图谱——谁在连、频率多高、单次连接多长。相比在网络设备上抓包分析,这种方式部署快、可随时撤除、且不依赖网络团队配合。它再次印证了四层代理的本质:因为不理解内容,它反而能服务于任何内容。

四层与七层的选择,最终可以压缩成一句判断:需要理解请求内容才做得了的事,交给七层;只需要"把字节搬到正确的机器",四层更快也更通用。第三章的扩容战役到这里全部收官:应用流量七层分发,数据库入口四层接管,整条链路有了统一的入口、统一的健康摘除与统一的观察面。

本节要点回顾

  • stream 是传输层代理:不能改内容,但什么协议都能转;
  • 数据库入口统一到 Nginx,主从切换不再惊动应用;
  • stream 后端必须限源,allow/deny 是最低配置;
  • 压测验收看 P99、错误率与 upstream 耗时占比,平均值不足为凭。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U