CentOS 7.9 上一台 nginx 1.20 反向代理两台 PHP 后端,网关日志里每秒都冒出几条 upstream timed out (110: Connection timed out),可同一时刻后端 access 日志显示这次请求只跑了三秒,监控面板上五零四的比例已经到一成。
一开始怀疑是「健康检查间隔太长,一台已经半死的节点仍留在上游列表里,轮询到它的请求全部超时」,查了一圈,方向不对。
把日志和配置对完,能解释这个现象的基本就这几处。nginx 与后端之间那一跳的内网设备静默丢包,三次握手能成但响应包回不来,计时一到就按超时处理。还有一种是后端把耗时写进了自己的日志,可它其实一直卡在 MySQL 上,请求早就被判超时,进程还在继续跑。
少数情况下是到上游的 keepalive 连接被中间设备提前掐断,nginx 复用了失效连接,请求发出去没人应答。
从网关日志里找出是哪个上游在超时
awk '$9==504{print $7,$9,$NF}' /var/log/nginx/access.log | tail -20
直接在机器上打后端接口,量真实耗时
curl -o /dev/null -s -w '%{http_code} %{time_total}\n' http://10.0.3.21/index.php
再往下抓包确认响应包到底有没有回来
tcpdump -i eth0 -nn host 10.0.3.21 and port 80 -c 200 -w /tmp/up.pcap
等前面都确认过,再按层拆分超时时间并关掉盲目重试
sed -i 's/proxy_read_timeout .*/proxy_read_timeout 15s;/' /etc/nginx/conf.d/api.conf && nginx -t && nginx -s reload
别只看报错消失,验证要多做一步。压测十分钟内五零四比例降到千分之一以下,access 日志里不再出现 timed out,curl 实测耗时与日志中的 upstream_response_time 相差不到一秒。
容易忽略的是,只把 proxy_read_timeout 调大,连接会占得更久,先分清后端是慢还是根本没回包,再决定改哪一侧。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。
