
1. 网络故障模拟到底在模拟什么第一次接触“网络故障模拟”这个词很多人脑子里浮现的是拔网线、关路由器这类物理操作。但真正在生产环境里做故障排查的人都知道物理断网反而是最容易定位的问题——它足够粗暴足够明显。真正让人头疼的是那些“网看起来是通的但服务就是时好时坏”的场景。TCP 连接偶尔超时、UDP 包莫名其妙丢失、延迟忽高忽低、吞吐量上不去这些问题不会给你任何显式报错只会让你在日志里反复怀疑人生。网络故障模拟的核心目标就是在可控的环境里主动制造这些“隐性故障”然后用抓包工具把故障发生时的数据流完整记录下来通过分析 TCP 和 UDP 的行为特征反推出问题根因。它解决的不是“网断了怎么办”而是“网没断但服务质量不达标问题出在哪一层”。这套方法适合谁运维工程师、后端开发、测试工程师、SRE以及任何需要跟网络打交道的人。你不需要是网络专家但需要具备基本的 TCP/IP 概念知道 IP 地址、端口、协议这些词是什么意思。如果你曾经对着curl超时的终端窗口发呆或者被“偶发 502”折磨过那这套东西就是为你准备的。我自己的经历是早年做后端服务时遇到一个“每天凌晨三点左右接口成功率下降”的问题监控上看不出任何异常服务器负载正常数据库正常但就是有少量请求超时。后来用抓包加故障模拟的方式才定位到是某个中间层在特定时间窗口做了连接回收导致 TCP 连接被意外重置。这个问题如果只靠看日志可能永远找不到。2. 抓包工具选型与故障模拟环境搭建2.1 为什么是 tcpdump 加 Wireshark 这套组合抓包工具的选择其实不多主流就是 tcpdump、Wireshark、tshark以及一些商业工具。我的建议是在目标机器上用 tcpdump 抓包把 pcap 文件拉到本地用 Wireshark 分析。这个组合看起来老派但它是经过时间验证的最稳方案。原因有几个。第一tcpdump 几乎在所有 Linux 发行版上都有不需要图形界面资源占用极低在生产环境里抓包不会给系统带来明显负担。第二Wireshark 的协议解析能力是目前最强的TCP 流跟踪、往返时间统计、重传分析这些功能做得非常成熟。第三pcap 文件是标准格式你抓下来的包可以反复分析也可以分享给同事一起看。有人会问为什么不直接在服务器上用 tshark 分析。我的经验是tshark 适合做自动化脚本里的快速过滤但真正要深入分析一个 TCP 会话的完整生命周期图形界面的 Wireshark 效率高得多。你可以用鼠标点几下就看到 TCP 流的时序图这在命令行里需要敲很多命令才能达到同样效果。注意在生产环境抓包前务必确认抓包范围和时间窗口。全量抓包在高流量场景下会迅速占满磁盘也可能涉及敏感数据。建议用-c限制包数量用-w指定输出文件并提前规划好过滤条件。2.2 故障模拟环境的三种搭建方式故障模拟环境不需要多复杂关键是要能复现问题。我常用三种方式按复杂度从低到高排列。第一种本机回环加流量控制工具。在 Linux 上用tctraffic control命令可以直接对网卡施加延迟、丢包、乱序等影响。比如你想模拟 200ms 延迟和 5% 丢包一条命令就能搞定。这种方式最简单适合快速验证。第二种容器网络加命名空间。用 Docker 或直接使用 Linux network namespace创建两个隔离的网络端点中间用tc或netem模块施加故障。这种方式比本机回环更接近真实场景因为流量确实经过了虚拟网卡和协议栈的完整路径。第三种虚拟机或独立测试机。如果条件允许用两台虚拟机通过虚拟交换机连接在虚拟交换机或虚拟机上施加故障。这种方式最接近生产环境但搭建成本也最高。我个人的建议是日常排查和验证用第一种就够了只有在需要模拟复杂网络拓扑时才上第二种或第三种。下面重点讲第一种的具体操作因为它最实用。2.3 用 tc 命令制造可控的网络故障tc是 Linux 内核自带的流量控制工具配合netemnetwork emulator模块可以模拟延迟、丢包、重复包、乱序、损坏包等多种故障。这些故障正好对应了 TCP 和 UDP 在实际网络中可能遇到的各种异常。先看几个核心命令。模拟固定延迟tc qdisc add dev eth0 root netem delay 200ms这条命令给 eth0 网卡出口方向加上 200ms 固定延迟。如果你想模拟延迟抖动可以写成delay 200ms 50ms表示延迟在 150ms 到 250ms 之间波动。模拟丢包tc qdisc add dev eth0 root netem loss 5%5% 的丢包率。这个比例在局域网里算很高了但在广域网或无线网络里并不罕见。模拟乱序tc qdisc add dev eth0 root netem delay 50ms reorder 25% 50%这条命令的意思是25% 的包会被乱序发送其中 50% 的乱序包会立即发送不经过延迟队列。乱序对 TCP 的影响很大因为它会触发快速重传机制。清除所有规则tc qdisc del dev eth0 root每次做完实验记得清除规则否则会影响后续所有网络通信。我就曾经忘记清除结果后面调试另一个问题时被残留的延迟规则误导了很久。提示tc命令需要 root 权限。在容器里使用时需要确保容器有NET_ADMINcapability。如果是在自己的开发机上做实验建议先在一个不重要的网卡或虚拟网卡上操作避免影响正常上网。3. TCP 抓包分析的核心看点3.1 三次握手阶段的异常识别TCP 的问题很多都能从三次握手阶段看出端倪。正常的握手是 SYN、SYN-ACK、ACK 三个包在 Wireshark 里过滤tcp.flags.syn1就能看到所有握手包。如果只看到 SYN 没有 SYN-ACK说明服务端没有响应。可能原因包括服务端端口没监听、防火墙拦截、服务端 backlog 队列满了。这时候你要看 SYN 包是否有重传以及重传的间隔是否符合 TCP 的指数退避规律。如果看到 SYN 之后跟着 RST说明服务端明确拒绝了连接。RST 包在 Wireshark 里过滤tcp.flags.reset1。常见原因是端口没开或者服务端主动拒绝了连接。如果 SYN-ACK 重传多次才收到 ACK说明客户端到服务端的链路有丢包或者客户端处理能力不足。这时候要结合tcp.analysis.retransmission过滤器看重传情况。我遇到过一个典型案例某服务在高峰期出现大量连接超时抓包发现 SYN 包发出后SYN-ACK 在 1 秒后才回来而且有多次重传。进一步分析发现是服务端的 accept 队列溢出导致部分 SYN 包被丢弃。这个问题在应用层日志里完全看不到只有抓包才能定位。3.2 数据传输阶段的吞吐量与窗口分析握手完成后进入数据传输阶段这时候重点看几个指标吞吐量、往返时间、窗口大小、重传率。Wireshark 自带的 TCP Stream Graphs 功能非常好用。打开一个 TCP 流在菜单里选择 Statistics - TCP Stream Graphs - Time Sequence (Stevens)你能看到序列号随时间的变化曲线。正常情况下这条曲线是平滑上升的如果出现水平段说明有停顿如果出现下降说明有重传。窗口大小反映了接收方的处理能力。如果窗口频繁降到零说明接收方应用层读取速度跟不上数据在接收缓冲区里堆积。这时候你要检查接收方的应用代码看是否有阻塞操作。往返时间RTT的波动能反映链路质量。Wireshark 在 TCP 流的专家信息里会给出 RTT 统计。如果 RTT 突然增大可能是链路拥塞也可能是中间设备做了流量整形。重传率是最直观的指标。在 Wireshark 里过滤tcp.analysis.retransmission统计重传包数量占总包数量的比例。局域网内重传率应该接近零广域网内一般不超过 1%。如果超过 5%说明网络质量已经严重影响到 TCP 性能了。3.3 四次挥手与连接重置的排查连接关闭阶段的问题同样值得关注。正常的四次挥手是 FIN、ACK、FIN、ACK。如果看到 RST 代替了 FIN说明有一方强制关闭了连接。RST 包的出现通常意味着应用层进程崩溃、连接被中间设备重置、或者一方在收到数据时发现连接已经不存在了。在 Wireshark 里你可以右键点击 RST 包选择 Follow TCP Stream看看 RST 之前的数据流是什么样的。有一个容易被忽略的细节TIME_WAIT 状态。主动关闭连接的一方会进入 TIME_WAIT持续 2MSL通常 60 秒。如果短时间内大量连接进入 TIME_WAIT会占用端口资源。抓包时如果看到大量 FIN 包来自同一台机器就要考虑是不是连接池配置不合理或者短连接使用过于频繁。4. UDP 抓包分析的特殊性4.1 UDP 没有握手问题更隐蔽UDP 是无连接协议没有握手、没有确认、没有重传。这意味着 UDP 的问题比 TCP 更难发现因为协议本身不提供任何可靠性保证。你发出去的包可能到了可能没到可能到了但顺序乱了协议层不会告诉你。抓 UDP 包时Wireshark 不会像 TCP 那样给你分析重传和窗口。你需要自己判断发出的包和收到的包是否匹配序列号是否连续时间间隔是否均匀。对于基于 UDP 的应用协议比如 DNS、DHCP、RTP、自定义游戏协议通常会在应用层定义自己的序列号或请求 ID。分析时要结合这些字段来判断丢包和乱序。4.2 用抓包判断 UDP 丢包位置UDP 丢包可能发生在三个位置发送端没发出去、中间网络丢了、接收端没收到。抓包可以帮助你缩小范围。在发送端抓包确认包确实发出了。在接收端抓包确认包是否到达。如果发送端有、接收端没有说明丢在中间网络。如果两端都有但应用层没处理说明是接收端应用的问题。这里有个实操技巧在两端同时抓包用时间戳对齐。Wireshark 支持导入多个 pcap 文件并合并分析。你可以把发送端和接收端的抓包文件都打开用相同的过滤条件对比很快就能看出哪些包丢了。注意UDP 抓包时要注意缓冲区大小。高流量场景下如果抓包工具的缓冲区不够大可能会丢包导致你误以为网络丢了包。用tcpdump -B参数可以调整缓冲区大小单位是 KB。4.3 UDP 故障模拟的典型场景用tc模拟 UDP 故障时效果比 TCP 更直接。因为 UDP 没有重传机制你施加 10% 丢包应用层就会实实在在感受到 10% 的丢包。我做过一个实验用 UDP 传输音视频数据施加 3% 丢包和 50ms 抖动。结果发现音质明显下降出现断续和杂音。抓包分析发现丢包集中在特定时间段进一步排查是中间某台设备的缓冲区溢出导致的突发丢包。这个实验说明UDP 应用对网络质量的敏感度远高于 TCP。TCP 遇到丢包会重传用户可能只是感觉慢了一点UDP 遇到丢包用户直接感受到的就是卡顿、花屏、断音。所以做 UDP 应用时要么在应用层做前向纠错要么接受一定程度的服务质量下降。5. 常见故障场景与排查速查5.1 连接超时类问题排查路径连接超时是最常见的网络故障表现。排查路径可以按以下顺序进行排查步骤抓包过滤条件观察重点可能原因确认 SYN 是否发出tcp.flags.syn1 ip.dst目标IPSYN 包是否存在本机路由问题、防火墙拦截确认 SYN-ACK 是否返回tcp.flags.syn1 tcp.flags.ack1是否有响应服务端未监听、服务端防火墙检查重传tcp.analysis.retransmission重传次数和间隔链路丢包、服务端过载检查 RSTtcp.flags.reset1是否有重置服务端拒绝、中间设备干扰这个表格我放在工位上遇到连接问题就按顺序过一遍基本能覆盖八成以上的场景。5.2 吞吐量低下的分析思路吞吐量上不去但连接是通的这种情况通常和窗口、延迟、丢包三者有关。TCP 的吞吐量理论上限是窗口大小除以往返时间。如果窗口是 64KBRTT 是 100ms那吞吐量上限就是 640KB/s大约 5Mbps。想提高吞吐量要么增大窗口要么降低 RTT。抓包时重点看 Window Size 字段。如果窗口一直很小说明接收方缓冲区不够。如果窗口够大但吞吐量还是低看是否有大量重传。重传会严重拉低有效吞吐量因为每个重传包都意味着之前发的包白发了。还有一个容易被忽略的点Nagle 算法和延迟确认的交互。Nagle 算法会合并小包发送延迟确认会推迟 ACK 发送两者同时开启时可能导致额外的延迟。抓包时如果看到小包被合并且 ACK 有 40ms 左右的延迟基本可以确认是这个原因。解决办法是设置 TCP_NODELAY 选项。5.3 偶发性故障的抓包策略偶发性故障是最难抓的因为你不确定什么时候会发生。我的策略是长时间低速率抓包配合环形缓冲区。具体做法是用 tcpdump 的-C和-W参数让抓包文件按大小滚动。比如tcpdump -i eth0 -w /tmp/capture.pcap -C 100 -W 10 -s 96这条命令的意思是每个文件最大 100MB最多保留 10 个文件循环覆盖。抓包长度限制为 96 字节只抓包头减少存储压力。这样可以在不影响系统性能的前提下持续抓取最近一段时间的流量。等故障发生后再从环形缓冲区里找到对应时间段的包。提示-s 96只抓包头对于分析 TCP 标志位和序列号足够了。如果需要分析应用层内容把-s设为 0 抓完整包但要注意存储和隐私问题。6. 实操心得与避坑经验6.1 抓包位置决定分析结论同样一个故障在客户端抓包和在服务端抓包看到的可能完全不同。客户端看到的是“我发了请求但没收到响应”服务端看到的是“我根本没收到请求”。这两个结论指向的排查方向完全相反。我的习惯是在链路的两端同时抓包。如果条件不允许至少在离故障点最近的位置抓。比如怀疑是中间网络的问题就在防火墙两侧各抓一份。对比两份抓包很快就能定位丢包发生在哪一段。还有一个细节抓包网卡的选择。如果服务器有多张网卡要确认流量走的是哪张。用ip route get 目标IP可以查看路由走向。抓错网卡是新手常犯的错误抓了半天发现全是无关流量。6.2 时间同步是分析的前提两端抓包对比时时间戳必须同步。如果两台机器的时间差了几秒你根本无法判断包是在哪一端丢的。建议在实验开始前用 NTP 同步所有机器的时间。如果条件不允许至少在抓包时记录一个共同的时间参考点比如同时 ping 一个公共地址用 ping 的往返时间来估算时间偏差。Wireshark 在合并多个抓包文件时会保留各自的时间戳。如果时间不同步你可以在 Wireshark 里手动调整时间偏移。具体操作是Edit - Time Shift输入偏移量。这个功能在对比分析时非常有用。6.3 过滤器的正确使用姿势Wireshark 的过滤器分两种抓包过滤器和显示过滤器。抓包过滤器在抓包时生效用 BPF 语法显示过滤器在分析时生效用 Wireshark 自己的语法。两者语法不同不要混淆。常用的显示过滤器我整理了几个tcp.port 8080过滤特定端口ip.addr 192.168.1.100过滤特定 IPtcp.analysis.flags显示所有 TCP 异常标志tcp.time_delta 1显示与上一个包间隔超过 1 秒的包udp.length 1000显示长度超过 1000 字节的 UDP 包最后一条在分析 UDP 时特别有用因为大 UDP 包更容易被分片也更容易丢失。注意显示过滤器里的和eq是等价的!和ne是等价的。但tcp.port 8080会匹配源端口或目的端口为 8080 的包如果你想只匹配目的端口要写成tcp.dstport 8080。6.4 故障模拟的伦理边界最后说一个容易被忽略的问题故障模拟只能在你自己有权限的环境里做。在生产环境或者别人的网络上施加tc规则可能造成服务中断这是绝对不能碰的红线。我自己的做法是所有故障模拟实验都在本地虚拟机或隔离的测试环境里进行。实验前确认环境隔离实验后立即清除规则。如果需要在准生产环境验证必须提前通知相关方并准备好回滚方案。这套方法的价值在于它让你从“被动救火”变成“主动理解”。当你亲手制造过丢包、延迟、乱序并观察过 TCP 和 UDP 在这些条件下的行为再遇到真实故障时你的判断会快很多。抓包分析不是玄学它是一门需要反复练习的手艺。