LabHub
学习 学习路径 课程

Tomcat 与 nginx 运维

别做那种一见 502 就去调大超时的人

在 LabHub 中继续学习

一句话总结

502表示“收到了无效的响应”,504表示“在规定时间内没有收到响应”,如果把这两个混在一起,只会不断延长超时时间,浪费几个小时。

流程图: 确信错误的原因 · 无效的响应 · 规定时间内没有响应 · 把502定性为“上游死了”。

为什么这是问题?

在应对障碍中耗费最多时间的不是找不到原因,而是确信错误的原因。如果把502定性为“后端死了”,那么就重新启动后端,如果不行,就增加超时时间,如果还是不行,就增加服务器。这段时间一直没有动过真正的原因,即响应头大小或连接提前终止。

如果有数字,就会以这种确信为基础而改变。在访问日志中,从每个URL的响应时间排序和5xx分布中提取出来,在nginx错误日志中单独统计connection refused和upstream timed out,502和504就会自动消失。计算这两个数字所需的时间是1分钟。

502和504是另一回事

代码 含义 nginx 发这个的时候 常见原因
502 Bad Gateway 上游收到了无效的响应或拒绝连接 拒绝连接、解析响应失败、连接提前终止 后端故障、队列饱和、响应头大于缓冲区
504 Gateway Timeout 上行端在规定时间内没有响应 proxy_read_timeout超出 慢查询,外部联动延迟,锁定等待
503 Service Unavailable 无可用上游 所有服务器都被排除在故障处理之外 全部故障、健康判断错误

在这里最常出错的地方就是这个。 把502定性为“上游死了”。

规范所说的502是“网关收到了来自上游的无效响应” 不是“上游没有回应”。虽然有回应,但也有502是因为代理处理不了的情况。 代表性的情况是响应头比代理缓冲区大的情况

这种情况如此糟糕的原因:上游的健康检查一直正常。 因为健康检查响应的头衔很小。所以“服务器没问题,但只有特定用户出现502”。 那个特定用户一般通过SSO登录,是cookie很大的用户

proxy_buffer_size       16k;
proxy_buffers         4 32k;
proxy_busy_buffers_size 64k;

还有一定要记住的:502会发生,延长超时时间没有任何效果。 超时是504的故事。把这两个混在一起的话会浪费几个小时。

诊断梯子——curl五个步骤

症状模糊时按照这个顺序缩小范围。特别是第5步将搜索空间缩小一半。

# 1. 이름이 풀리는가
getent hosts api.example.com

# 2. 포트가 열려 있는가 (ping 은 이 환경에서 안 되니 쓰지 않는다)
curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 3 http://api.example.com/

# 3. 상태코드와 헤더
curl -sSI https://api.example.com/health

# 4. 시간 분해
curl -s -o /dev/null -w 'dns=%{time_namelookup} conn=%{time_connect} \
tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' https://api.example.com/

# 5. ★ 프록시를 건너뛰고 업스트림에 직접, 원래 Host 헤더를 유지한 채
curl -sSI -H 'Host: api.example.com' http://127.0.0.1:8080/health

5次结果的解释是核心。

**-H 'Host: ...'除去这个的话,这个比较毫无意义。**如果存在虚拟主机路由的情况 因为Host不同的话,就会直接去其他应用程序。

4次的时间分解也很有用。

至少读取GC日志

[2026-08-19T02:14:33.221+0900][12.334s] GC(41) Pause Full (System.gc()) 486M->402M(512M) 812.443ms

在这一行中要读的内容。

判断标准不是绝对值,而是趋势和模式

OOM出现的时候

java.lang.OutOfMemoryError根据种类,措施完全不同。

信息 意思 第一次措施
Java heap space 臀部不足或漏水 臀部堆放分析。随便增加-Xmx的话,漏水就照样
Metaspace 类元数据不足 通过重复部署怀疑类加载器泄漏。重新启动后观察
GC overhead limit exceeded 大部分时间都花在GC上,但几乎无法回收 实际上是漏水。垃圾处理
unable to create native thread 线程生成失败 不是Hype,而是OS的限制/线程泄漏。线程dump

最后一句特别让人困惑。如果是OOM,但不是臀部问题-Xmx如果上传的话反而会变差(臀部变大的话线程用内存会减少)。

而且只有在垃圾场爆炸的那一刻才能收到。 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<경로>如果不提前放进去的话 如果能重现的话,到时候见吧”,但一般情况下不会重现。

重新启动之前留下证据

应对障碍的原则是优先恢复。但是,在按下重新启动按钮前1~2分钟的话 这三个是可以确定的。

PID=$(pgrep -f tomcat | head -1)
jcmd $PID Thread.print            > /tmp/threads_$(date +%H%M%S).txt
jcmd $PID GC.heap_info            > /tmp/heap_$(date +%H%M%S).txt
cp /opt/tomcat/logs/catalina.out    /tmp/catalina_$(date +%H%M%S).out

如果珍惜这1~2分钟,就永远找不到原因。 如果“先重新启动就行了”重复三次,第四次即使重新启动也行不了, 那时没有任何证据。

在日志中实际要抽取的东西

从访问日志中立即提取的四个。

  1. 状态代码分布 — 5xx 从什么时候开始增加的?
  2. 响应时间上位URL — 哪里慢?
  3. 按上游分列的错误分布—只有特定服务器有问题吗?
  4. 分堂请求次数——流量急剧增加是原因还是结果?

4号很重要。虽然可能是因障碍而请求增加的原因, 也有因为用户不行,所以反复刷新而增加的结果的情况很多。 仔细看开始时间就能区分开来。

在现场相遇的样子

最糟糕的形式是**“服务器没问题,但只有特定用户收到502”**。健康检查响应因为头衔很小,所以总是通过,只有部分用户的会话cookie很大,才会通过代理缓冲区收到502。这时监控仪表板都是绿色的,用户咨询是唯一的信号。

而且在重新启动之前需要养成留下证据的习惯。Hip dump和Thread dump在重新启动的瞬间消失,直到同样的故障再次出现之前无法知道原因。