《100个“反常识”经验15:Nginx 502排查:从应用到内核》

本期摘要

502 Bad Gateway 是 Nginx 最让人头疼的错误码之一。它不像 404 那样明确(文件不存在),而是表示"上游服务器返回了无效响应"。本文从一次真实故障切入,按"客户端 → Nginx → 应用 → 数据库"的链路分层排查,涵盖 upstream 超时、连接数耗尽、缓冲区不足、PHP‑FPM 崩溃、内核 backlog 溢出等常见原因。读完你就能在收到 502 告警时,快速定位是 Nginx 自身问题、后端应用问题,还是系统层面问题。

一次让人怀疑人生的故障

某个促销日,业务高峰,大量用户反馈页面打不开。Nginx 日志里全是:

text

复制代码
[error] 12345#0: *987654 connect() failed (110: Connection timed out) while connecting to upstream

502 扑面而来。运维同事第一反应:"后端服务挂了?"但检查应用进程,一切正常;检查数据库,也无异常。重启 Nginx 后短暂恢复,几分钟后又出现 502。

折腾了两个小时,发现问题出在 Nginx 与 PHP‑FPM 之间的 keepalive 配置不当,加上 upstream 响应过慢,导致连接池被耗尽。

502 的本质

Nginx 作为反向代理,接收到客户端请求后,需要向 upstream(后端应用服务器)发起新的请求。如果 Nginx 无法得到有效的 HTTP 响应,就会返回 502。

常见的上游类型:

  • HTTP 后端(Tomcat、Gunicorn、PHP‑FPM、Java 应用)

  • FastCGI(PHP‑FPM)

  • uWSGI(Python)

  • 代理服务器本身

排查四步法

第一步:查看 Nginx 错误日志

bash

复制代码
tail -f /var/log/nginx/error.log

错误日志会给出具体原因,常见错误包括:

日志关键字 含义 排查方向
connect() failed (110: Connection timed out) 连接上游超时 upstream 服务不可达或响应慢
connect() failed (111: Connection refused) 上游端口没开 upstream 服务未启动或挂掉
upstream timed out (110: Connection timed out) 读取响应头超时 upstream 处理太慢
no live upstreams 所有上游都挂了 检查 upstream 健康状态
client intended to send too large body 请求体过大 调大 client_max_body_size

第二步:检查 upstream 服务状态

如果是 HTTP 代理,直接 curl 测试:

bash

复制代码
curl -v http://127.0.0.1:8080/health   # 假设 upstream 在 8080 端口

如果超时或拒绝连接,问题在后端应用。

如果是 PHP‑FPM:

bash

复制代码
systemctl status php7.4-fpm
netstat -an | grep 9000   # 检查端口监听

第三步:检查 Nginx 与 upstream 之间的连接

连接数是否耗尽:

bash

复制代码
ss -an | grep 8080 | wc -l   # 看连接数
netstat -an | grep 8080 | grep TIME_WAIT | wc -l

如果 TIME_WAIT 堆积过多,调整内核参数或 Nginx keepalive。

upstream 响应时间:

在 Nginx 日志中记录请求时间:

bash

复制代码
log_format timed '$remote_addr - $request_time - $upstream_response_time - $request';
access_log /var/log/nginx/access.log timed;
  • $request_time:Nginx 处理总时间

  • $upstream_response_time:上游服务器响应时间

如果 $upstream_response_time 很大,说明后端慢;如果不大但 $request_time 大,可能是 Nginx 发送响应给客户端慢(网络问题)。

第四步:系统层面检查

net.core.somaxconn(监听队列长度)

bash

复制代码
sysctl net.core.somaxconn   # 默认 128,高并发时容易溢出

检查 Nginx backlog 配置

bash

复制代码
listen 80 backlog=1024;     # 增加队列容量

文件句柄限制:

bash

复制代码
ulimit -n                  # 查看当前进程可打开文件数
# 修改 /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535

常见场景及解决方案

场景 日志特征 解决方案
PHP‑FPM 响应慢 upstream timed out, 但 curl 能通 调大 fastcgi_read_timeout,优化 PHP 代码
后端服务连接池满 connect() failed (110: Connection timed out) 增加后端服务连接池,或调大 Nginx proxy_connect_timeout
TIME_WAIT 爆炸 ss 看到大量 TIME_WAIT 开启 proxy_http_version 1.1proxy_set_header Connection "",启用 keepalive
请求体过大 client intended to send too large body 增加 client_max_body_size
backlog 溢出 listen queue overflow 系统日志 增大 net.core.somaxconn 和 Nginx backlog
僵尸 upstream 被标记为 down no live upstreams 检查健康检查配置,必要时用 max_fails=0 禁止标记为 down

永久防范方案

1. 合理设置超时参数

nginx

复制代码
http {
    proxy_connect_timeout 5s;      # 连接上游超时
    proxy_send_timeout 10s;        # 发送数据超时
    proxy_read_timeout 10s;        # 接收响应超时
    fastcgi_read_timeout 60s;      # FastCGI 专用
}

2. 启用 upstream keepalive

nginx

复制代码
upstream backend {
    server 127.0.0.1:8080;
    keepalive 32;                 # 保持空闲连接数
}

server {
    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://backend;
    }
}

3. 使用健康检查

商业版 Nginx 支持主动健康检查。开源版可通过 max_failsfail_timeout 控制:

nginx

复制代码
upstream backend {
    server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
}

4. 监控与告警

  • 监控 Nginx error.log 中 502 的数量

  • 监控 upstream_response_time 的 P99 值

  • 监控 Nginx 连接数、TIME_WAIT 数量

下期预告

《100个"反常识"经验16:Docker容器退出,日志里什么都没有》

相关推荐
yoyo_zzm1 小时前
Laravel9.x新特性全解析
数据库·mysql·nginx
思茂信息2 小时前
CST软件如何进行参数化扫描?
运维·开发语言·javascript·windows·ecmascript·软件工程·软件需求
计算机安禾2 小时前
【Linux从入门到精通】第31篇:防火墙漫谈——iptables与firewalld防护指南
linux·运维·php
下一页盛夏花开2 小时前
ubuntu 20中安装QT以后出现红色空心断点
linux·运维·ubuntu
yoyo_zzm4 小时前
Laravel8.x新特性全解析
数据库·nginx
sxgzzn5 小时前
光伏数智化综合平台:让光伏电站运维更智能、更高效
运维
乌托邦的逃亡者6 小时前
Linux中如何检测IP冲突
linux·运维·tcp/ip
一曦的后花园7 小时前
linux搭建promethes并对接node-exporter指标
linux·运维·服务器