跳转至

Wireshark 抓包实战:定位 nginx 502 / 504 根因的完整排查链路

上周帮一个客户查 nginx 502,问题拖了三天——前两个运维看日志只看到 502,没有更多线索。我上去抓了个包,10 分钟定位:是后端 Java 应用开了 keepalive 但没设心跳,nginx 的 keepalive 连接过了空闲超时被中间防火墙切掉,复用时报 502。只看日志永远查不出来,必须抓包。

为什么 502/504 必须抓包

nginx 错误日志里 502 通常只写:

upstream timed out (110: Connection timed out) while connecting to upstream

110 是 Linux 内核错误码,翻译成人话就是"连不上"。但为什么连不上,可能原因有十几个:

  • 后端进程崩溃了
  • 后端端口变了(健康检查没同步)
  • 中间防火墙切了 keepalive 连接
  • TCP TIME_WAIT 堆积占满端口
  • MTU 不匹配导致大包丢失
  • DNS 解析慢了

日志看不出来,抓包才能看见是谁发了 RST、谁超时了、有没有分片丢包。

第一步:用 tcpdump 抓原始包

生产服务器通常没有 Wireshark GUI,先用 tcpdump 抓到 .pcap 文件,再拉到本机分析。

# 抓 nginx 与后端的通信(假设后端在 8080 端口)
tcpdump -nn -i any 'port 8080' -w /tmp/nginx.pcap &
# 用 & 放后台,复现一次 502,然后 kill %1 停抓

关键参数说明:

  • -nn 不解析域名和端口名,直接显示 IP 和数字端口,速度快且不触发 DNS 查询干扰
  • -i any 抓所有网卡,适合多网卡环境
  • -w file.pcap 保存原始包,后续用 Wireshark 打开

抓完记得及时停,不然 /tmp/nginx.pcap 会越来越大,压垮磁盘。

第二步:用 tshark 命令行过滤关键包

不需要 GUI 时,tshark 是最快的过滤工具。

# 只看 RST 包(谁发出来的决定了是客户端还是服务端问题)
tshark -r /tmp/nginx.pcap -Y 'tcp.flags.rst == 1' -T fields -e ip.src -e ip.dst -e tcp.seq

# 只看重传包(网络拥塞或丢包的铁证)
tshark -r /tmp/nginx.pcap -Y 'tcp.analysis.retransmission' -T fields -e ip.src -e ip.dst

# 只看 TIME_WAIT 状态的包
tshark -r /tmp/nginx.pcap -Y 'tcp.flags.fin == 1' -T fields -e ip.src -e ip.dst -e tcp.seq

# 只看大于 1460 字节的 TCP 载荷(MTU 问题信号)
tshark -r /tmp/nginx.pcap -Y 'tcp.len > 1460' -T fields -e ip.src -e ip.dst -e tcp.len

这四个过滤器覆盖了 80% 的 nginx 502/504 根因。

第三步:拉到本机用 Wireshark 深度分析

把 .pcap 文件拖到本机 Wireshark,重点看三个地方。

3.1 Follow TCP Stream —— 一锤定音

找到任意一条 nginx ↔ 后端的 TCP 会话,右键 → Follow → TCP Stream。

你会看到完整的六步对话:

Client (nginx)              Server (backend)
SYN ─────────────────────────────────────→
←──────────────────────────────────── SYN-ACK
ACK ─────────────────────────────────────→
DATA (POST /api/xxx) ──────────────────→
                              (无响应,30s 后)
Client sends RST ─────────────────────→

最后这一行 RST 如果是 nginx 发的 → 后端超时(504);如果是后端发的 → 后端主动断连(502)。

3.2 Endpoint 统计 —— 看流量分布

Statistics → Endpoints,按字节数排序,一眼看出哪台机器是流量异常方。

如果后端 IP 的出站字节数接近 0 但入站正常 → 后端没有响应数据。

3.3 Conversations 视图 —— 看连接模式

Statistics → Conversations → IPv4,切换显示为 TCP,可以看到:

  • 连接总数
  • 每个连接的 Duration(持续时长)
  • Packet 数 / Byte 数

翻车信号:大量连接 Duration 为 0,说明都是瞬时建立就断开的,典型 keepalive 配置错误或防火墙切连。

四个实战场景速查

场景 A:RST 来自后端

现象:Wireshark 里 nginx 发 SYN,后端回 RST。

原因:后端服务挂了,或端口监听地址不对(比如只监听了 127.0.0.1 没听 0.0.0.0)。

验证命令:

ss -tlnp | grep 8080
# 应该看到 LISTEN 0.0.0.0:8080,不是 127.0.0.1:8080

场景 B:RST 来自 nginx

现象:握手成功,nginx 发数据后长时间无响应,最后 nginx 发 RST。

原因:后端处理超时,nginx proxy_read_timeout 默认 60s,超时主动断。

修复方向:

location / {
    proxy_read_timeout 120s;      # 给后端更多时间
    proxy_send_timeout 120s;
    proxy_connect_timeout 10s;
}

场景 C:TIME_WAIT 堆积

现象:ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn 看到 TIME-WAIT 几千个。

原因:nginx 作为主动关闭方,短时间内大量建连断连,TIME_WAIT 来不及回收。

影响:新连接建不上,表现为间歇性 502。

修复:

# Linux 4.19+ 安全启用(内核有 SYN 攻击检测保护)
sysctl -w net.ipv4.tcp_tw_reuse=1
# 扩大 ephemeral port 范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

场景 D:MTU 黑洞

现象:小包能通,大包不通;telnet 能连上但 curl 超时。

验证:

# 看本地 MTU
ip link show | grep mtu

# 抓 SYN 包看 MSS 协商值(MTU - 40 = MSS)
tcpdump -nn -i any 'tcp[tcpflags] & tcp-syn != 0' -c 10

修复:两端 MTU 对齐,或开启 PMTU 发现(net.ipv4.tcp_mtu_probing=1)。

tshark 一键排障脚本

把上面四个场景串成一个脚本,一条命令出结论:

#!/bin/bash
# wh-examine.sh - Wireshark 502 快速诊断
PCAP=${1:-/tmp/nginx.pcap}
[ ! -f "$PCAP" ] && echo "用法: $0 <pcap文件>" && exit 1

echo "=== 1. RST 来源 ==="
tshark -r "$PCAP" -Y 'tcp.flags.rst == 1' -T fields -e ip.src -e ip.dst 2>/dev/null | head -5

echo -e "\n=== 2. 重传统计 ==="
tshark -r "$PCAP" -Y 'tcp.analysis.retransmission' 2>/dev/null | wc -l | xargs echo "重传包数:"

echo -e "\n=== 3. TIME_WAIT 相关 FIN 包 ==="
tshark -r "$PCAP" -Y 'tcp.flags.fin == 1' 2>/dev/null | wc -l | xargs echo "FIN 包数:"

echo -e "\n=== 4. 大包 (>1460B) 检查 ==="
tshark -r "$PCAP" -Y 'tcp.len > 1460' 2>/dev/null | wc -l | xargs echo "大包数:"

echo -e "\n=== 5. 异常连接(Duration=0 的 TCP 会话)==="
tshark -r "$PCAP" -Y 'tcp' -T fields -e ip.src -e ip.dst 2>/dev/null \
  | sort | uniq -c | sort -rn | head -5

排查流程速查表

症状 优先看什么 关键过滤器 / 命令
间歇性 502 RST 来源 tcp.flags.rst == 1
固定 URL 超时 504 TCP Stream 长度 Follow TCP Stream
高峰期才 502 TIME_WAIT 堆积 ss -tan \| awk '{print $1}'
小包通大包不通 MTU / MSS tcp.len > 1460
连接建立后无响应 重传包 tcp.analysis.retransmission

写在最后

nginx 502/504 的本质是网络层发生了什么,应用层只记录了结果。抓包是把结果逆推到原因的唯一可靠方法。学会 Follow TCP Stream 和这四个过滤器,80% 的排障不再需要猜。