实战排查生产环境 Nginx upstream timed out (110) 偶发 504 与长连接调优
一、生产环境故障背景与现象分析
在微服务架构或典型的前后端分离部署中,Nginx 作为入口网关承担着反向代理、负载均衡和 SSL 卸载等核心职责。在日常高并发或大促流量激增场景下,运维团队经常会收到告警系统派发的 HTTP 504 Gateway Time-out 报警,查看 Nginx 的 error.log,通常会伴随如下典型错误:
2026/11/09 07:15:22 [error] 14829#14829: *894102 upstream timed out (110: Connection timed out) while connecting to upstream, client: 192.168.10.45, server: api.internal.cloud, request: "GET /api/v1/order/query HTTP/1.1", upstream: "http://10.244.3.18:8080/api/v1/order/query", host: "api.internal.cloud"
与常见的业务处理超时(如慢 SQL 或外部依赖阻塞导致的响应读取超时 upstream timed out while reading response header from upstream)不同,这里的错误明确指出了阶段:while connecting to upstream,错误码是操作系统的 110 (ETIMEDOUT)。
这意味着 Nginx 在尝试与后端 Upstream 节点发起 TCP 三次握手时,发送了 SYN 包却在设定的超时时间(默认 proxy_connect_timeout 60s;)内未收到后端的 SYN+ACK,或者在重试多次后操作系统直接返回连接超时。
二、深层根因探究:为什么在连接建立阶段超时?
在排查 Linux 生产环境下的连接超时时,必须深入到 TCP/IP 协议栈与内核参数层面。通过线上抓包(tcpdump)与内核套接字分析(ss -lnt),主要根因可归纳为以下几类:
- 后端服务器半连接队列(SYN Backlog)或全连接队列(Accept Queue)溢出:当后端服务短时间内涌入大量短连接建连请求,若后端应用的
somaxconn或backlog较小,或者 Worker 线程被慢请求占满未能及时调用accept(),全连接队列会被迅速填满。此时 Linux 内核默认行为是直接静默丢弃后续到达的 SYN 握手包(防 SYN 洪水并期望客户端指数退避重试),导致 Nginx 端发生建连超时。 - 短连接风暴导致 TIME_WAIT 耗尽与端口复用冲突:Nginx 默认配置对 Upstream 使用 HTTP/1.0 且不保持长连接(即每个请求都新建 TCP 连接并随即断开)。高 QPS 下,每秒会产生数万个短暂的 TCP 连接。即使开启了
net.ipv4.tcp_tw_reuse,如果中间经过 NAT 网关且开启了tcp_tw_recycle(或 PAWS 时间戳校验冲突),会导致来自同一 IP 的新 SYN 包被后端静默丢弃。 - 云环境 Pod 弹性扩缩容或 DNS 缓存陈旧:在容器编排或微服务集群中,后端 Pod 销毁后,若 Nginx 内部仍缓存了失效的旧 IP,发往已不存在主机的 SYN 包由于无法收到任何响应(或黑洞路由),必然会持续等待直至
proxy_connect_timeout耗尽。
三、基于 Upstream Keepalive 连接池的深度调优实战
针对短连接风暴与建连耗时问题,最根本且高效的解决方案是在 Nginx 与后端服务之间引入 HTTP 长连接池,复用已建立的 TCP 链路,彻底消除握手开销。
1. 配置 Nginx Upstream Keepalive 指令
在 Nginx 配置文件中,仅仅在 upstream 块里写 keepalive 是不够的,必须同时在 location 块中将 HTTP 协议版本升级为 1.1 并清空 Connection 头部:
upstream backend_cluster {
server 10.244.3.18:8080 max_fails=3 fail_timeout=10s;
server 10.244.3.19:8080 max_fails=3 fail_timeout=10s;
# 每个 Nginx worker 进程保留的空闲长连接最大数量
keepalive 256;
# 允许复用的最大请求数,防止内存泄漏或连接老化
keepalive_requests 50000;
# 空闲连接保持时长
keepalive_timeout 65s;
}
server {
listen 80;
server_name api.internal.cloud;
location /api/ {
proxy_pass http://backend_cluster;
# 必须显式指定 HTTP/1.1,默认 1.0 不支持长连接
proxy_http_version 1.1;
# 清除 Connection 头部以启用长连接(默认 Nginx 会将其设为 close)
proxy_set_header Connection "";
# 优化超时时间:连接超时不宜设得过长,生产环境建议 3s~5s 以便快速失败重试
proxy_connect_timeout 3s;
proxy_read_timeout 30s;
proxy_send_timeout 15s;
# 允许在遇到连接超时或错误时,快速将请求转移到下一台健康后端
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
# 传递客户端真实信息
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
2. 内核连接队列参数协同优化
为了配合长连接池并彻底解决突发流量下的连接堆积,后端宿主机与容器节点的内核参数需同步调整:
# 查看当前系统的全局最大监听队列限制
sysctl net.core.somaxconn
# 提高全连接队列上限(建议生产环境调整至 4096 或更高)
sudo sysctl -w net.core.somaxconn=8192
# 调大半连接 SYN 队列容量
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=8192
# 启用安全重用 TIME_WAIT 套接字(仅适用于出站客户端连接)
sudo sysctl -w net.ipv4.tcp_tw_reuse=1
# 调大系统可分配的本地临时端口范围
sudo sysctl -w net.ipv4.ip_local_port_range="10240 65535"
四、调优效果验证与监控指标观察
完成上述长连接配置与参数调优后,通过 nginx -s reload 进行平滑热重载,并通过以下手段进行生产压测验证:
- TCP 连接状态监控:在 Nginx 节点执行
ss -ant | awk '{print $1}' | sort | uniq -c,观察到原先大量处于TIME_WAIT的连接数从数万级别急剧下降至数百个,而ESTABLISHED状态的连接保持平稳复用。 - 504 错误率清零:在 Grafana 监控大盘上,Nginx 的 Upstream 请求响应耗时 P99 显著降低(由于免去了每次建连的 1 个 RTT 延迟),
upstream_connect_time维持在 0.000~0.001 秒区间,偶发性的 504 错误彻底绝迹。 - 后端负载明显减轻:后端 Web 容器(如 Tomcat/Gunicorn)无需频繁分配与销毁 Socket 句柄,CPU 软中断(
si)占比下降约 30%,整体集群吞吐量得到质的飞跃。
总结:生产环境中排查网关层与后端交互的超时异常,必须精准区分“连接超时”与“读写超时”。针对 while connecting to upstream,优先建立 HTTP/1.1 Keepalive 长连接池并压低 proxy_connect_timeout 结合快速失败重试,是保障高可用与低延迟的最优雅架构实践。
- 点赞
- 收藏
- 关注作者
评论(0)