🧩 场景题题干(面试官常用表述)
用户反馈:页面一直报 502。
你是后端值班同学,如何快速排查并恢复服务?
如果恢复后还会再次出现,你会怎么做长期治理?
💡 一句话思路:网关 502 是“网关到上游失败”的结果。排查要从入口层向上游逐跳推进:Nginx/网关 → 应用实例/端口 → 进程/线程池/连接池 → 下游(DB/Redis/MQ) → 最近变更。
📚 学习正文:如何系统排查 502 / 503?
建议把排障过程分成两段:
1) 先止血(控制影响面与错误率)
2) 再定位(用日志+指标+验证命令闭环)
1) 现象与状态码含义(先把“现象”说清)
502 Bad Gateway : 代理请求上游失败(连接失败/非法响应/协议问题)
503 Unavailable : 上游不可用(无可用实例/健康检查失败/被熔断)
504 Timeout : 等上游超时(排队/慢 SQL/下游超时)
⚠️ 重点:状态码是入口观察到的结果,不等于根因。必须用“证据链”说服自己与面试官。
2) 总体排查路线(从入口逐跳推进)
入口(Nginx/网关)
↓
上游可达性(DNS/端口/TLS)
↓
应用状态(进程/日志/线程池/连接池/GC)
↓
下游依赖(DB/Redis/MQ/第三方)
↓
最近变更(发布/配置/证书/流量)
3) 先止血:让系统先“可用”再深挖
- 确认影响范围:全量/部分?是否某个接口?是否集中在某些实例?
- 快速止血:摘除异常实例、回滚最近发布、开启降级/限流/熔断。
- 再定位:错误率下降后,用日志+指标做根因定位与复现验证。
4) 常见根因速查表(看到日志就能快速归类)
- connect() failed (111: Connection refused):上游端口未监听/进程挂了/安全组或防火墙拦截/端口配错。
- no live upstream / 503:服务发现/注册异常、健康检查失败、实例被摘除、熔断打开。
- upstream timed out / 504:线程池排队、慢 SQL、锁等待、下游超时导致应用迟迟不返回。
- upstream sent invalid header:协议/响应头异常、header 太大、代理缓冲区限制。
- TLS/证书相关:证书过期、SNI/协议版本不兼容、网关到上游握手失败。
5) 定位方法:最常用的“命令 + 证据链”
💡 目标:不是“猜原因”,而是把问题收敛到:
哪一个接口?
哪一批请求?
哪一台 upstream 实例(IP:PORT)?
是连接失败、无实例、还是超时?
如果是超时:卡在应用哪一层(线程池/连接池/DB/Redis/MQ/第三方)?
5.1 先用 Nginx Access Log 定位到“具体接口 + 具体 upstream”
⚠️ 关键前提:你的 access log 必须带上 upstream 相关字段,否则无法精确定位到哪台上游实例。建议在 Nginx 加上(示例字段,按环境调整):
log_format main '$remote_addr $request $status '
'rt=$request_time urt=$upstream_response_time uct=$upstream_connect_time '
'uaddr=$upstream_addr ustatus=$upstream_status '
'reqid=$request_id';
你要从 access log 里拿到 3 个关键信息:
1) 哪个 URI/接口在大量报错(定位到具体接口)
2) 报错对应的 upstream_addr 是哪些(定位到具体实例)
3) request_time / upstream_response_time 分布(判断是否超时/排队)
# 1) 快速看 502/503 是否集中在某些 URI(示例:按 $7 取 URI,实际按你的 log_format 调整)
grep ' 502 ' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -nr | head
# 2) 看 502 是否集中在某些 upstream(需要 access log 有 uaddr 字段)
grep ' 502 ' /var/log/nginx/access.log | grep 'uaddr=' | sed -n 's/.*uaddr=\([^ ]*\).*/\1/p' | sort | uniq -c | sort -nr | head
# 3) 抽样一条 502 请求,看它打到哪个 upstream、耗时多少
grep ' 502 ' /var/log/nginx/access.log | tail -n 5
5.2 再用 Nginx Error Log 判断“属于哪类失败”
常见错误日志关键词 → 推断方向:
- connect() failed (111: Connection refused) → 进程/端口/安全策略
- no live upstream / upstream server temporarily disabled → 无可用实例/健康检查/熔断
- upstream timed out (110: Connection timed out) → 上游慢/排队/下游慢
- upstream sent invalid header → 协议/响应头/代理缓冲区
# 1) 在网关或同网段机器:验证上游是否可达
curl -v http://UPSTREAM_IP:PORT/actuator/health
# 2) 看网关错误日志(示例路径,按环境调整)
tail -n 200 /var/log/nginx/error.log
# 3) 上游机器:确认端口监听
lsof -i :8080
netstat -anp | grep 8080
# 4) 上游机器:查看服务日志/是否频繁重启(systemd 示例)
systemctl status your-service
journalctl -u your-service -n 200 --no-pager
5.3 追到“具体上游实例”后,怎么定位到“具体问题点”?
你已经从 access log 拿到 upstream_addr(例如 10.0.0.12:8080),接下来要在那台机器/那个容器上回答:
它是挂了?卡死了?还是被下游拖慢了?
- 进程与端口:是否存活、是否监听、是否频繁重启(OOM/CrashLoop)。
- 应用日志:是否有异常栈、超时、连接池耗尽、熔断触发;是否能按 requestId/traceId 关联。
- 线程池排队:Tomcat/业务线程池队列是否暴涨(排队会直接表现为 504/timeout)。
- 连接池耗尽:DB/Redis/http client 连接池是否耗尽(会导致请求阻塞)。
- GC/CPU:是否频繁 Full GC、CPU 飙高导致吞吐下降。
# 线程/CPU:找 Java PID & 热线程(Linux)
ps -ef | grep java
top -Hp <PID>
printf '%x\n' <TID>
jstack <PID> | grep -n "nid=0x..." -n
# 连接数/FD(典型:FD 打满导致各种连接失败)
ulimit -n
lsof -p <PID> | wc -l
# 应用健康检查(建议接入 actuator/health)
curl -s http://127.0.0.1:8080/actuator/health
5.4 进一步下钻:判断是不是“下游把上游拖慢”
如果 Nginx 侧看到的是 timeout/RT 飙升,往往不是网关问题,而是:
应用线程在等 DB/Redis/MQ/第三方,或在等锁/等连接池。
- MySQL:慢 SQL、锁等待、连接数打满、主从延迟。
- Redis:慢查询/大 key、阻塞命令、网络抖动、实例故障转移。
- MQ:消费堆积导致同步链路变慢(或重试风暴)。
- 第三方:超时重试策略不合理(无退避)导致雪崩。
💡 面试官喜欢的“闭环一句话”:“我先用 access log 精确定位到报错的 URI 与 upstream_addr,再用 error log 判断失败类型,然后到对应实例上用日志/指标/线程栈把根因收敛到线程池/连接池/下游慢点,最后用修复+治理把问题永久解决。”
⚠️ 面试官最想听到的:
我看到的日志是什么?
我做了什么验证?
验证结果是什么?
它如何支持“根因结论”?
6) 解决策略:短期修复与长期治理
短期:摘流异常实例 / 回滚变更 / 限流降级 / 关停高危接口 / 必要时扩容
长期:SLO 与告警 / 灰度与自动回滚 / 超时&重试治理 / 线程池隔离 / 容量评估 / 复盘机制