“recv() failed (104: connection reset by peer)”表示连接已建立且请求已发出,但后端在返回响应头时强制发送rst包中断连接,nginx捕获后返回502;其本质是后端“假死”(进程存活但异常终止连接),非宕机或网络失败。

“recv() failed (104: Connection reset by peer)” 是什么信号
这行日志不是连接失败,而是连接已建立、请求已发出、后端也接收到了,但在返回响应头(response header)过程中,后端进程突然关闭了连接——TCP 层发出了 RST 包,强制中断通信。Nginx 捕获到这个异常后,无法继续读取响应,只能返回 502。
为什么它指向“假死”而非“宕机”
真宕机(如进程退出、端口未监听)通常表现为 "Connection refused" 或 "No route to host";而 "recv() failed (104)" 意味着:
- 后端进程仍在运行,端口仍处于 LISTEN 状态
- Nginx 成功完成三次握手,甚至已把请求体发过去
- 后端在处理逻辑中卡住、崩溃于中间态(如 JVM 死锁、PHP-FPM worker 在 close() 前被 kill)、或主动调用 socket.close() 但未等响应写完
- 最典型场景:脚本超时被强制终止(PHP 的 max_execution_time 触发)、Java 应用线程阻塞在数据库锁或外部调用、Go 的 http.Server.WriteTimeout 被触发后提前关闭连接
结合上下游日志交叉验证关键线索
单看 Nginx 这条日志不够,必须同步检查:
- php-fpm.error.log 或 supervisord 日志:找 "WARNING: script timed out"、"child exited on signal TERM"、"failed to write to stdout"
- 应用层日志(如 Spring Boot 的 logback):搜索报错前最后几秒的堆栈,特别关注 InterruptedException、SocketException: Broken pipe、java.lang.OutOfMemoryError: unable to create new native thread
- 系统级痕迹:执行 dmesg -T | grep -i "killed process" 看是否 OOM Killer 杀过进程;用 strace -p $(pgrep -f 'your-app') -e trace=close,write,sendto 观察是否在 write 响应时被中断
快速复现与隔离验证方法
不要只等线上偶发,可主动模拟验证:
- 在后端加一段可控阻塞代码(如 Java 中
Thread.sleep(60000)),再用 curl -v http://nginx-host/path 触发,观察是否出现该 recv() failed - 从 Nginx 所在机器直连后端:curl -v http://127.0.0.1:8080/health,若返回 200 但耗时极长,说明服务活着但响应慢;若 curl 卡住数秒后报 "Connection reset by peer",基本锁定为假死
- 对比正常请求与失败请求的 upstream_response_time 字段:若失败请求的该值接近 Nginx 的
proxy_read_timeout(如 60s),大概率是后端超时被杀,而非网络问题











