常见 502/504 故障排查

常见 502/504 故障排查

写在前面

Nginx 作为反向代理,最常见的故障就是 5xx。本篇是故障排查速查表——涵盖 502/503/504 的所有常见原因、排查步骤、修复方案,以及 5 个真实生产故障的完整复盘。

一、5xx 状态码含义

状态码 含义 通常原因
500 Internal Server Error 后端代码错误
502 Bad Gateway Nginx 收不到 upstream 有效响应
503 Service Unavailable upstream 主动拒绝 / 限流
504 Gateway Timeout Nginx 等 upstream 超时

502 和 504 是 Nginx 反代的"特产"——直接访问后端没问题,通过 Nginx 就 502/504。

二、502 Bad Gateway

2.1 含义

Nginx 连不上 upstream,或者连接被切断,或者响应格式错误。

2.2 常见原因

  1. upstream 服务没启动
  2. upstream 端口配错
  3. upstream 主动断开连接(进程被 kill、OOM)
  4. 响应头过大(超过 proxy_buffer_size)
  5. 后端响应了不完整的 HTTP(程序 bug)
  6. upstream 用了 HTTP/2,Nginx 用 HTTP/1.1 代理

2.3 排查步骤

# 第一步:upstream 是否在监听
ss -tlnp | grep 3000
# LISTEN 0 511 0.0.0.0:3000 ...

# 第二步:从 Nginx 机器直接 curl upstream
curl -i http://10.0.0.1:3000/health
# HTTP/1.1 200 OK
# ...

# 第三步:Nginx 错误日志
tail -f /var/log/nginx/error.log
# 2024/07/15 14:23:01 [error] 1234#0: *5678 connect() failed (111: Connection refused) while connecting to upstream

2.4 经典错误信息

错误信息 原因
connect() failed (111: Connection refused) upstream 没起 / 端口错
connect() failed (113: No route to host) 网络层问题
upstream prematurely closed connection upstream 主动断开
invalid header 响应头格式错误 / 过大
no live upstreams upstream 全部标记为 down
SSL_do_handshake() failed 上游 HTTPS 证书问题

2.5 案例 1:响应头过大导致 502

现象:上传图片的接口偶发 502。

根因:proxy_buffer_size 默认 4k,响应头超过 4k 就报错。

修复:

location /api/upload {
    proxy_pass http://backend;
    proxy_buffer_size 16k;       # 调大
    proxy_buffers 8 32k;
}

2.6 案例 2:upstream OOM 被杀

现象:高峰时段 502 间歇性出现。

日志:

upstream prematurely closed connection

排查:

# 看 dmesg
dmesg | grep -i "killed process"
# [12345.678] Killed process 12345 (node) total-vm:1234567kB, anon-rss:524288kB

根因:Node 应用 OOM 被杀。

修复:

# docker-compose
services:
  api:
    deploy:
      resources:
        limits:
          memory: 1G

或者修代码的内存泄漏。

三、504 Gateway Timeout

3.1 含义

Nginx 等 upstream 响应超时。

3.2 常见原因

  1. 后端处理慢(数据库慢查询、外部 API 卡住)
  2. proxy_read_timeout 设得太短
  3. upstream 死锁
  4. upstream 等待中,CPU 100%

3.3 排查步骤

# 1. 后端响应慢在哪
# 从 Nginx 机器直接 curl
time curl http://10.0.0.1:3000/slow-api
# real    0m 30.01s  ← 超时!
# 2. 错误日志
tail /var/log/nginx/error.log
# *5678 upstream timed out (110: Connection timed out) while reading response header from upstream

3.4 经典错误信息

错误信息 原因
upstream timed out (110: Connection timed out) 整个连接超时
upstream timed out (110: Connection timed out) while reading response header 等响应头超时
upstream timed out while reading response 等响应体超时

3.5 案例 3:数据库慢查询导致 504

现象:下午 2 点开始批量 504,持续 20 分钟。

排查:

# Prometheus 看 MySQL
mysql_global_status_slow_queries

根因:一个定时任务在跑大报表查询,锁住 orders 表。

修复:

-- 给大查询设超时
SET SESSION MAX_EXECUTION_TIME = 30000;  -- 30 秒

-- 应用层加超时
SELECT /*+ MAX_EXECUTION_TIME(30000) */ ...

Nginx 端:

location /api/orders {
    proxy_pass http://order_service;
    proxy_read_timeout 35s;       # 比后端超时略长
}

3.6 案例 4:WebSocket 长连接 504

现象:WebSocket 连接保持 1 分钟后 504。

根因:proxy_read_timeout 默认 60s,WebSocket 长连接超时。

修复:

location /ws/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;        # 1 小时
    proxy_send_timeout 3600s;
}

四、503 Service Unavailable

4.1 含义

upstream 主动拒绝请求,或被限流。

4.2 常见原因

  1. 限流触发
  2. upstream 维护中
  3. upstream 启动中
  4. 健康检查失败

4.3 案例 5:健康检查失败导致 503

现象:部分请求 503,日志 no live upstreams。

根因:健康检查路径配错。

# 错误 - 后端没有这个路径
location / {
    health_check interval=5s uri=/health uri_is_redirect;
}

# 后端实际是 /api/health

修复:对齐路径。

location / {
    health_check interval=5s uri=/api/health;
}

五、4xx 错误

虽然不是 5xx,但生产中也常见,顺便讲讲。

5.1 400 Bad Request

5.2 403 Forbidden

5.3 404 Not Found

5.4 413 Request Entity Too Large

5.5 499 Client Closed Request

Nginx 特有:客户端主动断开请求。常见于响应太慢,客户端超时。

# 看 499 比例
awk '$9 == 499 {count++} END {print count}' /var/log/nginx/access.log

如果 499 多,说明:

六、系统化排查流程

6.1 五步排查法

Step 1: 看错误日志

tail -f /var/log/nginx/error.log | grep -E "(error|crit|emerg)"

Step 2: 看访问日志

tail -f /var/log/nginx/access.log | grep " 502 \| 503 \| 504 "

Step 3: 直接访问 upstream

# 绕过 Nginx,直接打后端
curl -i http://10.0.0.1:3000/api/orders

如果直接访问没问题,问题在 Nginx。

如果直接访问也 5xx,问题在后端。

Step 4: 看 upstream 状态

# 系统资源
top
free -h
df -h

# 后端进程
ps aux | grep node

Step 5: 网络层

# 端口通不通
nc -zv 10.0.0.1 3000

# TCP 连接
ss -tan | grep 3000

# 抓包
tcpdump -i any -nn -s 0 -w /tmp/cap.pcap host 10.0.0.1 and port 3000

6.2 配置健康检查

upstream backend {
    server 10.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:3000 max_fails=3 fail_timeout=30s;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_next_upstream error timeout http_502 http_503 http_504;
        proxy_next_upstream_tries 2;
        proxy_next_upstream_timeout 10s;
    }
}

自动 failover:一个 upstream 挂掉,流量自动转走。

6.3 配置错误页面

server {
    error_page 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
        internal;
    }
}

6.4 监控告警

# Prometheus blackbox_exporter
- name: nginx_errors
  rules:
    - alert: Nginx502High
      expr: |
        sum(rate(nginx_http_requests_total{status="502"}[5m])) > 1        
      for: 5m
      labels:
        severity: critical

    - alert: Nginx504High
      expr: |
        sum(rate(nginx_http_requests_total{status="504"}[5m])) > 1        
      for: 5m
      labels:
        severity: critical

七、Nginx 配置本身的错误

不是后端问题,是 Nginx 配错。

7.1 配置文件语法错误

nginx -t
# nginx: [emerg] unexpected "}" in /etc/nginx/nginx.conf:45

7.2 配置 reload 失败

nginx -s reload
# nginx: [error] invalid PID number "" in "/run/nginx.pid"
# 解决
sudo nginx -c /etc/nginx/nginx.conf

7.3 include 文件权限问题

nginx -t
# nginx: [emerg] open() "/etc/nginx/conf.d/api.conf" failed (13: Permission denied)
chmod 644 /etc/nginx/conf.d/api.conf
chown root:root /etc/nginx/conf.d/api.conf

八、调试技巧

8.1 debug 日志

error_log /var/log/nginx/debug.log debug;

需要编译时带 --with-debug。

8.2 tcpdump 抓包

tcpdump -i any -nn -A -s 0 port 443 and host 1.2.3.4 | head -100

8.3 strace 跟踪 worker

strace -p $(pgrep -f "nginx: worker") -e trace=network,read,write -f

⚠️ strace 性能影响大,只在调试时用。

九、故障应急手册

9.1 502 应急

#!/bin/bash
# emergency-502.sh

# 1. 看 upstream 状态
echo "=== Upstream Status ==="
systemctl status my-app || echo "App not running"

# 2. 看连接
echo "=== Connections ==="
ss -tan | grep 3000 | head -20

# 3. 看错误日志最近 100 行
echo "=== Recent Errors ==="
tail -100 /var/log/nginx/error.log

# 4. 重启 upstream
echo "=== Restarting App ==="
systemctl restart my-app

# 5. 验证恢复
sleep 5
curl -i http://10.0.0.1:3000/health

9.2 一键回滚

#!/bin/bash
# rollback.sh

# 备份当前配置
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak

# 用上一个版本
cp /etc/nginx/nginx.conf.last-working /etc/nginx/nginx.conf

# 测试
nginx -t && nginx -s reload

echo "Rolled back to last working config"

十、本篇小结

错误 含义 第一排查
502 收不到 upstream 响应 upstream 状态 / 端口
503 upstream 拒绝 / 限流 限流配置 / 健康检查
504 等 upstream 超时 proxy_read_timeout / 后端性能
499 客户端断开 后端响应慢
排查步骤 命令
看错误日志 tail /var/log/nginx/error.log
看 5xx 状态码 grep " 50[234] " access.log
直接打后端 curl http://upstream/api
看进程 ps aux | grep app
抓包 tcpdump -i any port 443

下一篇是 Nginx 系列的最后一篇,日志分析与可视化——用 GoAccess 和 ELK 找爬虫、分析流量模式。

参考资料