Nginx 错误码排查:499/502/504

Nginx 日志里最容易被误判的三个码是 499、502、504:它们看起来都在说“上游有问题”,但责任方完全不同。本章先讲清每个码的判定标准,再给出从 error.log 到上游应用日志的固定排查路径,最后说明哪些配置该调、哪些配置调了也没用。

四个码的责任划分

状态码含义责任方常见原因
499客户端在 Nginx 返回响应前关闭了连接客户端或上游上游响应太慢用户取消、前端超时比服务端短、用户刷新或关页面、压测客户端提前断开
502网关从上游收到了无效响应Nginx 与上游之间上游进程崩溃、端口没监听、upstream prematurely closed connection、协议不匹配
504上游在规定时间内没有响应上游应用proxy_read_timeout 到期、DB 或三方依赖慢、线程池满、长 GC
500 / 503应用内部错误 / 服务不可用上游应用或 Nginx应用抛异常;503 常见于 limit_connlimit_req 拒绝,或所有上游被标记为不可用

499 是 Nginx 侧记录的结果,不代表“一定是客户端的问题”——上游慢到客户端等不下去时,同样记 499,而根因在上游。

排查顺序

1. error.log 关键字    确认错的是哪一类:连接被拒 / 提前关闭 / 超时 / 拒绝
2. access.log 拆维度   $upstream_status、$upstream_response_time、$request_time、$upstream_addr
3. 定位到上游实例      按 upstream_addr 找出是哪台机器哪个端口
4. 上游应用日志        对应时间点的异常、GC、线程池、DB 慢查询
5. Nginx 自身容量      worker_connections、连接数、backlog 是否打满
6. 内核与网络          somaxconn、TIME_WAIT、端口范围、DNS 解析

第一步:在 error.log 里找关键字

tail -f /var/log/nginx/error.log
grep -E 'upstream prematurely closed connection|no live upstreams|upstream timed out|connect\(\) failed|Connection refused' /var/log/nginx/error.log | tail -50
error.log 关键字对应状态码含义与下一步
connect() failed (111: Connection refused)502上游没监听该端口:查进程是否存活、是否正在重启、端口是否被改
upstream prematurely closed connection while reading response header502上游进程崩溃或主动断开:查应用日志与 OOM 记录
upstream timed out (110: Connection timed out) while reading response header504上游处理超时:继续查应用慢在哪、依赖是否也超时
no live upstreams while connecting to upstream502 / 503后端全部被健康检查判为不可用:先恢复实例,再查健康检查配置
upstream sent too big header502上游响应头过大:调 proxy_buffer_sizeproxy_buffers
worker_connections are not enough连接被拒连接数不足:调 worker_connectionsulimit -n
client intended to send too large body413请求体超限:调 client_max_body_size

第二步:access.log 拆维度

前提是日志格式带上了上游字段,推荐下面这种:

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
                '$request_time $upstream_response_time $upstream_status $upstream_addr';

按这个格式,$9$status$10$request_time$11$upstream_response_time$12$upstream_status$13$upstream_addr

# 客户端看到的状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 上游返回的状态码分布——这是区分责任方的关键
awk '{print $12}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
# 最慢的 20 个请求:request_time、upstream_response_time、status、URL
awk '{print $10, $11, $9, $7}' /var/log/nginx/access.log | sort -rn | head -20
# 只看 499 与 504 的 URL 分布
awk '$9 ~ /^(499|504)$/ {print $9, $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 按上游实例聚合 5xx,找出是不是单台机器的问题
awk '$9 ~ /^5/ {print $13}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
字段组合判断
$status=499$upstream_response_time-请求可能还没到上游,或上游迟迟没返回响应头,客户端已经走了
$status=499$upstream_response_time 很大上游太慢,客户端等不及——责任在上游
$status=502$upstream_status-与上游的连接根本没建立成功,查端口与进程
$status=502$upstream_status=502上游自己返回了错误,查应用日志
$status=504$upstream_status=504proxy_read_timeout 到期,上游没在预算内响应
$request_time 远大于 $upstream_response_time慢在 Nginx 侧:限流排队、响应体大、客户端下载慢
$request_time$upstream_response_time 接近慢在上游应用

第三步:Nginx 自身与内核

nginx -T | grep -E 'worker_processes|worker_connections|keepalive |proxy_(connect|read|send)_timeout|proxy_next_upstream|limit_(conn|req)'
nginx -s reload                       # 平滑加载新配置,不中断已有连接
ss -s
ss -lnt | grep -E ':80|:443'
grep -i listenoverflows /proc/net/netstat
ulimit -n; cat /proc/sys/net/core/somaxconn; cat /proc/sys/net/ipv4/ip_local_port_range
指标判读
worker_connections × worker_processes可承载的连接上限;做反向代理时,每条客户端连接通常还要占用一条到上游的连接,keepalive 连接池能显著降低消耗
ListenOverflows / ListenDrops 大于 0accept 队列溢出,需要同时调大 somaxconnlisten backlog
TIME_WAIT 数量极高到上游未开启 keepalive,端口被快速消耗
limit_req 命中率升高不是故障而是保护生效,需要与业务确认拒绝策略

处置动作

  • 504 居多:先分清是全局变慢还是单实例变慢。单实例 → 摘除并保留现场排查;全局 → 扩容上游或优化慢依赖,同时确认应用侧的依赖超时预算。只放宽 Nginx 超时通常只是把 504 变成更长的等待。
  • 502 居多:检查上游进程是否崩溃、是否被 OOMKilled、是否在重启中端口未就绪;可在 Nginx 侧配置 proxy_next_upstream error timeout http_502;proxy_next_upstream_tries 2 做有限重试。
  • 499 居多:绝大多数是上游慢导致的用户放弃,先治上游;同时检查前端超时是否比 Nginx 短,避免客户端先超时而服务端还在白干。
  • 限流保护:用 limit_connlimit_req 防止单 IP 打满连接与带宽,用 proxy_connect_timeout 缩短故障节点的等待时间。

预防

  • access.log 必须包含 $request_time $upstream_response_time $upstream_status $upstream_addr,缺了字段故障时只能靠猜。
  • $upstream_response_time 的 P99、5xx 比例、499 比例分别设告警;按 $upstream_addr 维度做实例级告警,快速发现单实例劣化。
  • 上游健康检查加自动摘除,发布时优雅停机:先停止接收新请求,再等待在途请求处理完。
  • worker_connectionssomaxconnulimit -n 要一起调,只调其中一个通常没效果。

小结:502 是 Nginx 与上游之间的连接或响应问题,504 是上游超时,499 是客户端因上游慢而放弃;排查固定走“error.log 关键字 → access.log 的 upstream 字段 → 上游实例与应用日志 → Nginx 容量与内核”这条路;处置时优先摘除坏实例与优化上游,把超时调大只当作争取时间的临时手段。

笔记加载中…