线上服务最让人头疼的故障之一,就是网络丢包排查:接口时好时坏、偶发 502、延迟突然飙升,重启没用、看日志也不报错。本文以一次真实的跨可用区专线抖动事故为线索,带你用 ping、mtr、ss、tcpdump 把网络丢包和网络延迟的根因一步步定位出来,并给出可落地的监控与预防方案。
一、故障现场:接口为什么时好时坏
某天上午,客服群里开始零星反馈”下单偶尔失败”。监控面板一切正常:CPU、内存、连接池都没打满。但把网关访问日志拉出来看,确实有约 1.5% 的请求耗时从 30ms 跳到了 3 秒以上,并伴随少量 502。这恰恰是典型的网络丢包排查场景——单看应用层指标看不出问题,因为锅不在你这台机器,而在你和下游之间的链路上。
这类问题的共同特征是:有损、偶发、难以在单机复现。TCP 本身是可靠传输,但中间任何一跳(网卡、交换机、云厂商虚拟网络、跨可用区专线、公网)出现丢包,都会表现为重传、RTT 抖动和连接超时。下面我们按”由粗到细”的顺序逐层定位。
二、排查第一步:用 ping 和 mtr 画出丢包地图
不要一上来就抓包。先用 ping 确认”通不通、抖不抖”,再用 mtr(my traceroute)看清丢包发生在哪一段。mtr 会持续探测每一跳的丢包率和延迟,比一次性 traceroute 更有诊断价值。
# 连续 ping 100 次,看丢包率和延迟分布
ping -c 100 api.internal.example.com
# mtr:实时看到每一跳的丢包(Last/Avg/Best/Wrst 与 Loss%)
mtr -n -c 100 api.internal.example.com
# 只看汇总,适合写进故障报告
mtr -n -r -c 200 api.internal.example.com
读 mtr 输出有个关键经验:中间跳丢包但最后一跳不丢,往往不是真丢包。很多路由设备对 ICMP 做了限速(rate-limit),会故意不回探测包,造成”中间跳 Loss% 高”的假象。真正要关注的是目标地址那一跳,以及从某一跳开始之后所有跳都出现持续丢包——那才说明瓶颈就在此处。
三、是网络层还是应用层?用 ss 看 TCP 重传
确认链路有抖动后,下一步要区分:是网络真的在丢包,还是应用本身慢?看 TCP 重传率最直接。重传意味着包没被确认、被丢了,是网络丢包的铁证。
# 看某条连接的详细指标:重传、RTT、拥塞窗口
ss -i dst api.internal.example.com
# 系统级 TCP 重传计数(关注 TcpRetransSegs)
netstat -s | grep -i retrans
nstat -az TcpRetransSegs
# 计算重传率:重传段 / 发出的段
# 建议采集 interval 内的增量做比值
如果 TcpRetransSegs 在持续增长,说明这台机器发出的包确实在网络里丢了,问题在外部链路,而不是你的代码。此时再去纠结 JVM 调优或 SQL 优化,只会南辕北辙。
四、抓包定位:tcpdump 看包到底去哪了
当重传发生在特定目标或端口,用 tcpdump 抓一段时间的包,能看清是 SYN 丢了(建连失败)、还是数据传输中丢(RTT 抖动)。注意抓包要在客户端和服务端两边同时抓,对比哪一侧没收到对方的包,才能判断丢在哪段。
# 抓与下游服务的交互,存文件用 Wireshark 分析
tcpdump -i any -nn -s0 -w /tmp/netloss.pcap \
host api.internal.example.com and port 8080
# 实时看重传(TCP 重传包的标志:未带 SYN/FIN/PSH 的重复 seq)
tcpdump -i any 'tcp[tcpflags] & (tcp-rst) != 0' host api.internal.example.com
抓包后重点看三件事:是否有大量 Dup ACK(重复确认,预示丢包)、是否有 RTO 超时重传、RTT 是否在某个时间点集体跳变。集体跳变往往对应链路切换或抖动事件。
五、三大常见丢包点:MTU、云厂商限速、跨可用区
落地踩坑无数次后,我把常见的”网络丢包 / 网络延迟”根因归纳成下面三类,按出现频率排序:
| 丢包点 | 典型现象 | 定位命令 | 解法 |
|---|---|---|---|
| MTU / 分片 | 大包必丢、小包正常,VPN/隧道后高发 | ping -s 1472 -M do 逐步测最大不分包大小 | 调小接口 MTU 或开启 PMTU 发现 |
| 云厂商限速/安全组 | 流量高峰丢包、PPS 突增即 502 | 云监控”丢弃包”指标、安全组规则审计 | 提工单扩容、拆分可用区流量 |
| 跨可用区/专线抖动 | 深夜或割接期偶发延迟飙升 | mtr 末跳抖动、专线侧监控 | 多可用区冗余、客户端退避重试 |
其中 MTU 问题最容易被忽略:默认 1500 在直连以太网没问题,但一旦经过 GRE/IPsec/VXLAN 等隧道封装,外层头会吃掉几十字节,1500 的包就会被丢或分片,表现为”小请求正常、大响应超时”。用 ping -s 1472 -M do 一路调小就能复现。
六、真实案例:一次跨可用区专线抖动导致 502 雪崩
回到开头那次故障:mtr 显示末跳延迟在 20ms 与 800ms 之间剧烈摆动,TcpRetransSegs 同步上涨。结合云厂商通知,确认是连接两个可用区的专线在凌晨割接。下游服务在 B 区、网关在 A 区,跨区流量全部经过这条抖动的专线。
根因明确后,我们从两端下手。一是让核心调用尽量同可用区闭环,二是给跨区调用加有上限的指数退避重试,避免在抖动期把请求雪崩式堆起来:
import time, random
def call_with_backoff(fn, max_retries=3):
for i in range(max_retries):
try:
return fn()
except (TimeoutError, ConnectionError) as e:
if i == max_retries - 1:
raise
# 指数退避 + 抖动,避免重试风暴放大丢包
sleep = (2 ** i) * 0.1 + random.uniform(0, 0.05)
time.sleep(sleep)
这类网络层面的偶发故障,单靠应用层重试还不够。更稳的做法是参考Nginx 反向代理实战里的健康检查与熔断配置,把”持续不可达的下游”临时摘掉;同时用DNS 解析故障排查的思路确认不是解析层在抖动,再用服务器时钟漂移排查排除因时间错乱导致的 TLS/证书校验异常。三者常常结伴出现,要一起核对。
七、TCP 层优化:内核参数与重传策略
对于无法立刻消除的链路抖动,可以通过内核参数降低其影响。核心是缩短”判定丢包并重传”的等待时间,让 TCP 更快自愈:
# 减少重传超时,加快从丢包中恢复(默认值偏保守)
sysctl -w net.ipv4.tcp_retries2=5
# 提升初始拥塞窗口,小文件首屏更快(需内核支持)
ip route change default via 10.0.0.1 dev eth0 initcwnd 10 initrwnd 10
# 开启 TCP 快速打开(TFO),降低建连 RTT
sysctl -w net.ipv4.tcp_fastopen=3
注意:这些都是”缓解”而非”根治”。如果丢包率长期高于 0.1%,优先找网络团队或云厂商,而不是靠调参掩盖问题——调参只能让故障变得不那么刺眼,却会让根因更难被发现。
八、监控与预防:把丢包变成可观测指标
每次都靠出故障再排查太被动。建议把网络丢包和网络延迟前置成日常监控指标:用黑盒探测(如黑盒 exporter 或定时 mtr)持续打点,配合主机上的 nstat 重传计数,一旦出现异常立即告警。
# 用 node_exporter 暴露 TCP 重传,Prometheus 抓取
- job_name: node
static_configs:
- targets: ['10.0.0.10:9100']
# 关键告警规则:5 分钟内重传段占比超阈值
- alert: HighTcpRetransmit
expr: rate(node_netstat_Tcp_RetransSegs[5m]) / rate(node_netstat_Tcp_OutSegs[5m]) > 0.01
for: 5m
| 监控指标 | 采集方式 | 告警阈值建议 |
|---|---|---|
| TCP 重传率 | nstat TcpRetransSegs | > 1% 持续 5 分钟 |
| 到下游 RTT P99 | 黑盒 mtr / HTTP 探针 | 超过基线 3 倍 |
| 接口丢包(网卡) | ip -s link | RX/TX dropped > 0 增长 |
九、排查清单:下次再遇偶发超时按这个来
- 1. 确认范围:是所有请求还是特定下游?影响面决定排查方向。
- 2. 画链路:
mtr看末跳丢包率与抖动,区分”真丢包”和”ICMP 限速假象”。 - 3. 看重传:
nstat TcpRetransSegs是否上涨,确认是网络层问题。 - 4. 查 MTU:
ping -s 1472 -M do测最大不分包大小,排除隧道封装。 - 5. 双向抓包:客户端+服务端同时 tcpdump,定位丢在哪一侧。
- 6. 核对云与同区:云监控丢包指标、跨可用区调用是否可收敛。
- 7. 加退避与熔断:参考Nginx 502/504 排查的超时与重试配置,避免雪崩。
遇到偶发 502 和超时,第一反应别急着改代码。先把网络丢包排查这套链路走一遍——多数时候,真正的病灶在你看不见的那几跳上。把它变成可观测的指标,比事后救火划算得多。




