Skip to content

接口 502 与后端 OOM 终止

01 前置理论与核心机制

1. 502 错误的本质与排查误区

  • 网关层角色定界:502 Bad Gateway 表示 Nginx 作为反向代理网关,在尝试将请求转发给后端上游服务器(如 Tomcat、Python、Go 进程)时,收到了无效响应或无法建立连接。此时盲目重启 Nginx 无效,故障根源通常在于后端节点异常或中间网络中断。
  • Refused 与 Timeout 的定界:
  • Connection refused (连接拒绝):代表后端服务器的网络链路可达,但目标端口上无进程监听(服务进程已退出或监听端口配置错误)。
  • Connection timed out (连接超时):代表握手报文丢失或未及时响应,通常由防火墙拦截、路由不通或后端内核队列溢出所致。

2. 内核 OOM Killer 机制

  • 当系统物理内存耗尽,且 Swap 空间不足(或未启用)时,Linux 内核为保障系统基本运行,会触发 OOM (Out Of Memory) Killer 机制。
  • 内核会根据内部评分机制(oom_score),筛选出内存占用过高的进程,直接发送 SIGKILL(信号 9)将其强制终止。此时应用进程立即退出,Nginx 转发请求时无法连接该节点,便会向客户端返回 502 错误。

02 故障排查与恢复闭环 SOP

遇到线上接口大面积报 502 错误,按照“查网关日志 -> 测后端端口 -> 查内核日志”的链路执行排查:

步骤一:查看 Nginx 错误日志定界

优先查看 error.log 获取底层的连接报错原因:

# 查看最新的 Nginx 错误日志
tail -n 10 /var/log/nginx/error.log
  • 观测点:若日志中明确记录 connect() to 192.168.0.89:8080 failed (111: Connection refused),说明 Nginx 自身运行正常,但在向 192.168.0.89 节点转发请求时被对方内核拒绝。

步骤二:利用 nc 探测后端端口连通性

在网关服务器上,直接测试后端节点的业务端口状态:

# 探测后端节点的 8080 端口是否开放 (-z 仅扫描不发送数据,-v 输出详细信息)
nc -zv 192.168.0.89 8080
  • 定性:同样返回 Connection refused。由此可排除中间防火墙丢包(丢包通常表现为 timeout),确认是后端服务未处于监听状态。

步骤三:确认后端进程存活状态

登录报错的后端服务器(192.168.0.89),核查业务进程与端口状态:

# 检查 8080 端口的监听情况
ss -tulnp | grep 8080

  • 观测点:若无任何输出,说明后端业务进程已经异常退出。

步骤四:检查 dmesg 提取内核 OOM 记录

当进程在无明显应用层崩溃日志的情况下消失时,直接检查内核环形缓冲区日志:

# 打印内核日志并转换可读时间戳
dmesg -T | tail -n 20

  • 日志确认:若看到 Memory cgroup out of memory 或 Out of memory: Killed process 1855 (python3),即可确认后端业务发生了内存耗尽,触发了操作系统或容器的 OOM 限制,导致进程被内核强制终止。

步骤五:应急拉起止损与网关容错加固

生产环境故障优先恢复业务可用性: - 重启恢复:先在后端节点重新拉起业务进程应急(或扩容容器实例),验证接口恢复 200 OK,随后保留现场日志交由研发排查内存泄漏代码。 - 网关容错配置:在 Nginx 的反向代理配置中检查并补充自动故障转移指令:

# 当遇到连接错误(如refused)、超时或后端返回502时,自动将请求转发至上游其他健康节点
proxy_next_upstream error timeout http_502;

03 真实生产故障现象与案例库

案例 1:未分页慢查询引发的连续 OOM

  • 故障现象:某电商详情页接口偶发 502,运维登录节点发现 Java 进程消失,dmesg 确认为 OOM 被终止。
  • 排查与处置:手动拉起进程 10 分钟后再次触发 OOM。深入排查发现,某导出接口的 SQL 缺失分页限制,单次请求从数据库加载了上百万条记录至内存,迅速耗尽堆内存与物理内存。处置方案为:在网关层摘除故障节点,回滚问题版本,并配置 Nginx 的 proxy_next_upstream 确保单节点异常时流量平滑切换。

案例 2:Node.js 内存泄漏导致进程假死与 502

  • 故障现象:Node.js 服务偶发报 502,但登录机器执行 ss 发现端口仍在监听,dmesg 中也无 OOM 记录。
  • 根因与处置:应用层存在内存泄漏,内存逼近上限时触发 V8 引擎高频执行 Full GC(垃圾回收),导致主事件循环(Event Loop)长时间阻塞,无法处理新进的 TCP 连接队列,最终导致连接队列溢出被拒。处置方案为使用 pm2 配置内存上限自动重启(max_memory_restart)作为临时保护,并采集 Heap Snapshot 定位内存泄漏代码。

04 生产排障速查表(502 报错链路)

排查层级执行命令 / 观测点异常定性处置动作01 网关侧tail -f /var/log/nginx/error.log报错 Connection refused确认网关正常,定位报错的具体后端节点 IP02 网络侧nc -zv <IP> <端口>立即返回拒绝连接确认网络可达且未被防火墙丢弃,系端口未监听03 后端侧ss -tulnp | grep <端口>无任何输出确认后端业务进程已退出04 内核侧dmesg -T | grep -i "killed process"存在 OOM Killer 终止记录确认为内存超限被内核强制终止,优先拉起进程止损05 防御侧检查 nginx.conf 反向代理配置补充 proxy_next_upstream开启上游故障自动转移,屏蔽单点进程退出影响

05 面试应答参考话术

面试官问:“线上接口突然报 502 错误,你怎么排查?”

可以这样回答:接口报 502 Bad Gateway,说明 Nginx 作为反向代理网关无法正常连接后端的应用进程。此时盲目重启 Nginx 是无效的,必须沿着转发链路向后端排查。第一步:查看 Nginx 错误日志定界。

首先查看 /var/log/nginx/error.log。如果看到 Connection refused,说明 Nginx 在向某个后端节点转发请求时被直接拒绝了。为了确认状态,可以在网关上用 nc -zv 探测一下该后端节点的端口。如果同样返回 refused,说明网络是通的,但后端端口没有进程在监听。第二步:检查后端进程与内核日志。

登录对应的后端服务器,执行 ss -tulnp 查看端口,确认业务进程已经退出。如果应用日志里没有明显的崩溃报错,进程不会无故消失,我会立即执行 dmesg -T | tail -n 20 检查操作系统内核日志。如果看到 Out of memory 和 Killed process 记录,就可以确认:后端服务发生了内存泄漏或瞬时内存飙升,触发了系统或 Cgroup 的内存限制,被内核的 OOM Killer 强制终止了。第三步:应急止损与网关容错加固。

处理生产故障优先止损。我会在后端先把服务进程重新拉起来,验证接口恢复 200 OK 稳住线上业务,同时保留日志提交给研发排查内存问题。 此外,为了避免单节点崩溃直接导致前端用户看到 502,我会在 Nginx 配置中加上 proxy_next_upstream error timeout http_502;。配置该容错参数后,当某个后端节点再次异常退出时,Nginx 会自动将请求转发给上游其他健康的备用节点,实现故障对用户侧的透明屏蔽。

06 面试高频追问与应答预案

追问 1:在 Nginx 中开启 proxy_next_upstream 自动重试,会带来什么潜在风险?

  • 回答要点:
  • 非幂等接口的重复执行风险:如果客户端发起的是 POST 创建订单或扣款等非幂等请求,请求转发至节点 A 后,节点 A 已经完成了数据库写入,但在返回响应前发生崩溃。此时若 Nginx 触发自动重试将同一请求再次转发给节点 B,就会导致重复扣款或产生脏数据。
  • 规避策略:重试机制必须结合接口的幂等性设计。对于 GET、HEAD 等只读查询请求可安全开启重试;对于 POST、PUT 等写操作,需依赖业务层的幂等 Token 校验,或在 Nginx 中通过 proxy_next_upstream_methods 严格限制允许重试的 HTTP 方法(Nginx 默认仅对 GET/HEAD/PUT/DELETE 等重试,默认不包含 non_idempotent 的 POST,切勿盲目追加 non_idempotent 参数)。

追问 2:Nginx 报 502 和 504 错误有什么本质区别?

  • 回答要点:
  • 502 Bad Gateway:网关无法与后端建立连接(如后端进程退出返回 Connection refused),或连接异常断开、后端返回了不符合 HTTP 规范的无效响应。
  • 504 Gateway Timeout:网关已经与后端成功建立了连接并发送了请求,但后端业务处理耗时过长(例如执行慢 SQL 或等待第三方接口阻塞),超过了 Nginx 配置的 proxy_read_timeout 阈值,网关主动断开连接并返回 504。

07 后端 OOM 终止记录自动诊断脚本(check_502_oom.sh)

该脚本用于在后端服务器上执行,自动检索内核 OOM 终止记录、评估当前内存水位并列出高内存占用进程。

#!/usr/bin/env bash
# ==============================================================================
# 脚本名称: check_502_oom.sh
# 功能描述: 后端服务进程 OOM 终止记录自动诊断与内存状态巡检 (纯只读)
# 使用方式: 在报错对应的后端 Linux 服务器上以 root 权限执行
# ==============================================================================

set -o pipefail

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PLAIN='\033[0m'

echo -e "${BLUE}================================================================${PLAIN}"
echo -e "${BLUE}           底层系统 OOM Killer 记录与内存水位安全巡检           ${PLAIN}"
echo -e "${BLUE}================================================================${PLAIN}"

# 1. 检索 dmesg 中的 OOM 记录
echo -e "\n${YELLOW}[1/3] 正在扫描系统内核 dmesg OOM 终止记录...${PLAIN}"
OOM_LOGS=$(dmesg -T 2>/dev/null | grep -i -E "Out of memory|Killed process|Memory cgroup out of memory")

if [ -n "$OOM_LOGS" ]; then
    echo -e "  -> ${RED}[ALERT] 检测到内核 OOM Killer 触发记录!近期有以下进程因内存超限被强制终止:${PLAIN}"
    echo "$OOM_LOGS" | tail -n 5 | while read -r line; do
        echo -e "     ${RED}${line}${PLAIN}"
    done
    echo -e "  -> ${YELLOW}[诊断] 若被终止的进程包含业务服务 (Java/Python/Node/Go等),即为触发网关 502 的直接原因。${PLAIN}"
else
    echo -e "  -> ${GREEN}[OK] dmesg 内核日志中暂未发现进程被 OOM 终止的记录。${PLAIN}"
fi

# 2. 检查当前系统内存水位
echo -e "\n${YELLOW}[2/3] 当前系统物理内存与 Swap 水位评估...${PLAIN}"
free -m | awk '
NR==2 {
    total=$2; used=$3; free=$4;
    usage=used/total*100;
    printf "  -> 物理内存使用率: %.1f%% (Total: %d MB, Used: %d MB)\n", usage, total, used
    if(usage > 90) { print "     \033[0;31m[WARN] 物理内存使用率超过 90%,存在较高的 OOM 触发风险!\033[0m" }
}
NR==3 {
    if($2 > 0) {
        total=$2; used=$3;
        usage=used/total*100;
        printf "  -> Swap 交换分区 : %.1f%% (Total: %d MB, Used: %d MB)\n", usage, total, used
    } else {
        print "  -> Swap 交换分区 : 未启用 (当物理内存耗尽时将直接触发 OOM Killer)"
    }
}'

# 3. 筛选当前内存占用 TOP 3 的进程
echo -e "\n${YELLOW}[3/3] 当前存活进程物理内存占用排行 (TOP 3)...${PLAIN}"
ps -eo pid,pmem,rss,comm --sort=-rss | head -n 4 | awk '
NR==1 { printf "     %-8s %-6s %-12s %s\n", $1, $2, "RSS(KB)", $4 }
NR>1 { printf "     %-8s %-6s %-12s %s\n", $1, $2, $3, $4 }'

echo -e "\n${BLUE}================================================================${PLAIN}"
echo -e "${BLUE}巡检完毕。若确认为单点 OOM 导致 502,建议优化应用内存并在网关补充 proxy_next_upstream 容错配置。${PLAIN}"
echo -e "${BLUE}================================================================${PLAIN}"