后端假死是504或recv() failed (104)的主因:进程存活但失响应,需结合error.log中“while reading response header”等线索、抓包确认rst来源、比对$upstream_response_time与$request_time,并深入检查jvm gc、go协程泄漏或node.js事件循环阻塞。

后端服务假死是 Nginx 出现 504 或 recv() failed (104: Connection reset by peer) 的常见真凶——进程没挂,但已失去响应能力。排查不能只看“进程在不在”,而要聚焦在“它是否还能正常收发、处理请求”。
看日志定位假死发生阶段
打开 Nginx error.log,重点识别三类线索:
- while reading response header from upstream:说明连接已建好,但后端连第一个响应头都没发出来 → 很可能卡在初始化、数据库连接、锁竞争或 GC 中
- upstream prematurely closed connection:后端主动断连,常见于进程未崩溃但线程池耗尽、goroutine 泄漏、Node.js 事件循环阻塞
- 对比
$upstream_response_time和$request_time:
若前者有值(如2.987),后者远大于它(如62.341),说明后端确实返回了部分数据,但后续卡住;
若前者为-,说明后端根本没写出任何字节,大概率处于僵死或刚启动未就绪状态
抓包确认 RST 来源和时机
仅靠日志无法判断谁“掐断”了连接。必须抓包验证:
- 在 Nginx 服务器执行:
tcpdump -i any 'host 后端IP and port 后端端口' -w fake-dead.pcap - 复现问题后,用 Wireshark 打开,过滤
tcp.flags.reset == 1 - 检查 RST 包的源 IP:
→ 是后端地址?查其 JVM GC、Go 协程数、Node.js 事件循环延迟
→ 是中间防火墙或 LB?检查策略是否重置空闲连接
→ 是客户端?需排查前端超时设置或网关行为
查后端真实运行态,不止看进程和健康接口
“curl /health 返回 200”不等于服务可用。要深入运行时:
-
JVM 应用:查 GC 日志,看 Full GC 是否频繁且单次 > proxy_read_timeout;用
jstack <pid></pid>看是否有大量线程卡在BLOCKED(锁竞争)或WAITING(线程池满、DB 连接池空) -
Go 应用:运行
go tool pprof http://localhost:6060/debug/pprof/goroutine?debug=2,观察 goroutine 数量是否持续上涨;检查内存是否接近容器 limit 触发 panic 后 socket 被回收 -
Node.js:用
clinic doctor --on-port 3000检测事件循环延迟;检查是否有同步大计算(如 JSON.parse 大文件、正则回溯)阻塞主线程 - 通用信号:CPU 使用率低但请求堆积、线程/协程数异常飙升、健康检查通但业务接口全超时
验证是否被防火墙或连接池干扰
假死有时是“被误判”的结果:
- 检查 Nginx upstream 是否启用了
keepalive,而后端不支持 HTTP/1.1 持久连接 → 复用旧连接时遭遇 RST - 确认防火墙是否对健康检查路径(如
/health)放行,但对业务路径限速或拦截 → 健康检查通过,实际请求被丢包或延迟升高 - 查看后端日志中是否频繁出现
broken pipe、client closed connection或Connection reset,尤其集中在某类请求后











