一、开门见山:我被线上超时逼到抓包
某天下午3点,运维群里炸了:“线上订单服务响应超时,客户端等待15秒才返回500!”我第一反应查服务器日志:Nginx 200,PHP-FPM处理时间<2秒,MySQL查询平均20ms。后端看起来一切正常。客户端说“我们测了3次都超时,不是我们问题”。
这种时候,只有抓包能说清楚到底哪段网络在吞时间。我选了Wireshark,因为GUI直观,能快速过滤出重传、乱序、零窗口等关键信息。
二、我的抓包方案:两层过滤+三种分析视角
2.1 方案对比:tcpdump vs Wireshark
| 工具 | 优势 | 劣势 | 我的选择场景 |
|---|---|---|---|
| tcpdump | 轻量、可远程执行、脚本友好 | 分析需要命令行;过滤语法繁琐 | 服务端快速抓包后传回本地分析 |
| Wireshark | 图形化、协议解析强、统计丰富 | 需要图形界面;大文件会卡 | 本地分析pcap、定位问题 |
我的标准流程:服务端tcpdump抓5秒(用环形缓冲区以免爆盘)→ scp到本地 → Wireshark打开 → 用显示过滤器快速缩小范围。
2.2 完整抓包命令(版本:tcpdump 4.99.1, Wireshark 4.2.0)
# 服务端(Ubuntu 22.04, eth0网卡):
# 只抓对应端口,避免大量无关流量
# -i eth0 接口
# -s 0 抓完整包(默认65535)
# -C 200 单个文件200MB,-W 5 最多5个文件循环覆盖
# -w /tmp/order.pcap 写入文件
# port 8080 只抓目标端口8080的包
tcpdump -i eth0 -s 0 -C 200 -W 5 -w /tmp/order.pcap port 8080
抓5-10秒就够了(超时问题通常在首轮请求就有体现)。文件传到本地:
# 本地Mac
scp user@server:/tmp/order.pcap ./order.pcap
三、实战三步:用Wireshark过滤出问题包
3.1 先看概况:Statistics → Summary
打开文件后,第一眼看Average packet size和Packet length delta。我那次抓到的包:平均包长只有400字节,但有大量小于100字节的ACK包。再去IO Graphs看吞吐量趋势——发现每1秒左右出现一个缺口(无数据传输)。
3.2 核心过滤:TCP重传、乱序、零窗口
直接套用以下显示过滤器(Display Filter):
# 1. 所有TCP重传(包括快速重传)
tcp.analysis.retransmission
# 2. 所有重复ACK(可能指示乱序或丢包)
tcp.analysis.duplicate_ack
# 3. 零窗口通告(发送方停止发送)
tcp.analysis.zero_window
# 4. 窗口满(接收方暂时不能收)
tcp.analysis.window_full
# 5. 一次完整的HTTP请求+响应(过滤指定IP和端口)
ip.addr==10.0.0.1 and tcp.port==8080 and http
那次我执行 tcp.analysis.retransmission or tcp.analysis.duplicate_ack,直接看到37%的包是重传或DupACK。正常的局域网内重传率应该小于1%。
3.3 深入分析:通过跟踪TCP流查看详情
右键任意HTTP响应包 → Follow → TCP Stream,可以看到完整请求响应文本。我发现的异常:客户端发送请求后,服务端立即返回SYN+ACK,但数据包等了800ms才发出——这是典型的**Nagle算法延迟**?不,仔细看Seq和Ack:服务端在等客户端的ACK确认上一个数据段才发送下一段,说明TCP发送窗口太小。
// 伪代码模拟当时的交互(从Wireshark Time列看出):
// 0ms: Client -> Server: [PSH, ACK] Seq=1, len=1460
// 800ms: Server -> Client: [PSH, ACK] Seq=1, len=1460 (延迟8倍!)
// 原因:Server的TCP发送缓存填满,等待窗口更新
四、完整代码实现:自动抓包+分析脚本
我将过程脚本化,以后同类问题一键排查。
4.1 自动部署抓包脚本(bash+expect)
#!/bin/bash
# 用法:./capture.sh
SERVER=$1
PORT=$2
DUR=$3
ssh root@$SERVER "tcpdump -i eth0 -s 0 -C 100 -W 3 -w /tmp/cap.pcap port $PORT & sleep $DUR; kill %1"
scp root@$SERVER:/tmp/cap.pcap ./$(date +%Y%m%d_%H%M)_$SERVER.pcap
echo "Done: $(ls -lh *.pcap)"
4.2 生成分析报告(Python 3.10 + pyshark 0.6)
pyshark是Wireshark的命令行版tshark的封装,可以程序化分析:
# analyze.py
import pyshark
import sys
cap = pyshark.FileCapture(sys.argv[1], display_filter='tcp.analysis.retransmission')
print(f"File: {sys.argv[1]}")
print(f"Total retransmissions: {len(cap)}")
for i, pkt in enumerate(cap):
if i >= 5: break
print(f" Retransmit {i+1}: {pkt.sniff_time} - {pkt.ip.src}:{pkt.tcp.srcport} -> {pkt.ip.dst}:{pkt.tcp.dstport}")
cap.close()
4.3 压测重现问题(jmeter或wrk)
# wrk压测,模拟高并发
wrk -t4 -c200 -d30s http://10.0.0.1:8080/order
# 同时开另一个终端抓包
tcpdump -i eth0 -s 0 -C 100 -W 3 -w /tmp/highload.pcap port 8080 &
sleep 35; kill %1
五、效果数据:抓包前后耗时对比
| 场景 | 传统排查方式 | 用Wireshark抓包分析 | 提升 |
|---|---|---|---|
| 定位慢SQL | 开启general_log,分析半天 | 过滤MySQL Query或慢查询包,5分钟 | 减少95%时间 |
| 定位TCP重传 | 看服务端netstat统计、调用链 | 过滤tcp.analysis.retransmission,3分钟 | 减少90%时间 |
| HTTP超时归属 | 两边日志时间戳对不齐 | 抓包直接看到请求发出时间和响应到达时间 | 精准到毫秒 |
以本文开头案例为例:
- 传统方式:检查日志、ping、mtr、调优参数,3小时没解决。
- 用Wireshark:下载pcap后,输入过滤显示重传包,发现服务端到客户端方向的重传率20%+。进一步发现是客户端window太小(由于客户端TCP内核参数net.core.rmem_default设置过低导致每次只能收几千字节)。
- 定位时间:10分钟。
六、避坑指南(我踩过的5个坑)
坑1:捕获过滤器 vs 显示过滤器 傻傻分不清
Wireshark有两个过滤器:抓包前设置Capture Filter(基于BPF语法)和抓包后设置Display Filter。我一开始用显示过滤器写 port 80,但抓包时没设捕获过滤器,导致pcap文件巨大(包含所有包),打开卡死。
正确做法:抓包时就限制端口、IP、协议,显示过滤器只做精细筛选。
坑2:抓包网卡没开混杂模式
在云服务器上,弹性网卡默认不抓本机不相关的包。如果你的服务使用VIP或多网卡,务必确认tcpdump或Wireshark设置promiscuous mode。我在阿里云没开,只抓到发给本机MAC的包,结果漏掉本机经VIP出去的流量。解决方案:先检查tcpdump -i eth0 -e看目的MAC是否对。
坑3:大文件打开死机
一次抓了20分钟,pcap达8GB。Wireshark在笔记本上直接崩了。解决方案:使用 editcap 拆分文件,或者用 tshark -r big.pcap -Y "filter" -w small.pcap 预过滤后再打开。
# 先用tshark过滤出需要的流
tshark -r big.pcap -Y "tcp.port==8080" -w small.pcap
坑4:忽略时间戳精度
Wireshark默认显示微秒级时间戳,但如果你跨机器分析,需要保证各机器时间同步(NTP偏差<1ms)。我遇到过一次:两台服务器时间差2秒,导致抓包中的请求-响应时间计算完全错误。解决办法:抓包前确认NTP同步,或在抓包文件中手动调整时间偏移(Statistics→Time Shift)。
坑5:HTTPS流量抓了白抓
抓HTTP请求没问题,但HTTPS需要额外步骤。我一开始抓了全是TLS密文。正确姿势:要么在代理层解密(如mitmproxy、Burp),要么导出SSLKEYLOGFILE并加载到Wireshark。具体步骤:
# 服务端应用启动前导出SSLKEYLOGFILE
export SSLKEYLOGFILE=/tmp/sslkey.log
# 重启应用(如nginx)然后抓包
tcpdump -i eth0 -s 0 -C 100 -W 3 -w /tmp/https.pcap port 443
# 本地Wireshark打开pcap后,设置Preference→Protocols→TLS→(Pre)-Master-Secret log filename
七、总结
Wireshark不是屠龙刀,但遇到网络延迟、丢包、超时问题,它就是最趁手的一把手术刀。牢记三步:服务端轻量抓包 → 本地Wireshark打开 → 显示过滤器精确打击。把上面脚本放入你的工具箱,下次线上超时,直接上抓包,别再盲目调日志了。
附:Wireshark常用过滤速查表(我贴桌边)
| 需求 | 显示过滤器 |
|---|---|
| 所有TCP重传 | tcp.analysis.retransmission |
| 所有快速重传 | tcp.analysis.fast_retransmission |
| 零窗口 | tcp.analysis.zero_window |
| HTTP 500错误 | http.response.code == 500 |
| DNS查询超时 | dns.flags.response == 0 and dns.elapsed > 0.1 |