暗色模式

Linux 网络模拟:tc 与 netem 注入延迟、抖动与丢包

技术教程
2026-09-05
4
0
本文要点
  • netem 是 Linux 内核自带的网络模拟器(挂在 tc 的 qdisc 上):不用跨国拉专线,一条命令就能在回环接口上模拟出 100ms 延迟、±20ms 抖动、20% 丢包,用来提前验证应用在真实公网下的表现
  • tc 的流量控制模型:网卡出口有一个 qdisc(排队规则),生产网卡默认为 fq_codel;tc qdisc add dev lo root netem ... 就是把 netem 挂到回环接口上,实测输出 qdisc netem 8001: ... delay 100ms
  • 实测给 lo 加 100ms 延迟后 ping 回环地址:RTT 从 0.0x ms 变成 整整齐齐的 200ms——回环接口上每个包往返要各过一次 qdisc,所以单程 100ms 翻成 200ms
  • 抖动用 delay 100ms 20ms distribution normal 模拟,实测 6 个包 RTT 在 191~224ms 间波动,mdev 从 0.017 涨到 9.961
  • 丢包 loss 20% 是**单程**丢包率:lo 上往返两级联后实测 12 个包丢 5 个(41.7%),与理论值 1-(1-20%)²≈36% 同量级
  • 全部实验只在回环接口 lo 上进行,不碰生产网卡,不会把自己 SSH 断掉;tc qdisc del dev lo root 一键恢复;Ubuntu 24.04.4 实测,截图即真实输出

Linux 网络模拟:用 tc 与 netem 注入延迟、抖动与丢包

线上应用出问题,最常被甩锅的是"网络不好":异地机房间隔 200ms、跨境链路丢包重传、移动网络忽快忽慢。可本地开发环境走的是机房内部网络,1ms 都不到——于是很多只在真实公网下才会暴露的问题(TCP 窗口上不去、超时重试逻辑太激进、视频卡顿)在测试环境里永远复现不出来。

netem(Network Emulator)就是为解决这个问题而生的:它是 Linux 内核自带的网络模拟模块,挂在流量控制的 qdisc(排队规则)上,可以给流量注入人为的延迟、抖动、丢包、乱序、重复。不需要真跨网络,一台服务器就能模拟出"在 200ms 延迟、20% 丢包的链路上运行"的完整体验。

本文全部实验都在回环接口 lo 上进行——只影响本机自己 ping 自己的流量,不碰真实网卡,绝不会把自己远程连接的 SSH 断掉。实验环境为 Ubuntu 24.04.4(内核 6.8,iproute2 自带 tc),所有命令真实执行,截图即真实输出。

tc 与 qdisc:流量控制的基本模型

Linux 的流量控制(traffic control,tc)由三层组成:qdisc(排队规则,挂在接口上)、class(qdisc 内部的分类)、filter(把包分到各类的过滤器)。对模拟场景来说,只需要理解 qdisc:每个网卡出口(egress)都有一个 root qdisc 负责排队发送,netem 就是一个特殊的 qdisc,它不排队,而是在包通过时"做手脚"——延迟、丢包、乱序都在这一刻注入。

先看看生产网卡默认长什么样。用只读命令查看主网卡(演示机为 ens5)的 qdisc 与累计统计:

tc -s qdisc show dev ens5

实测输出中,配置行是 qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 ... target 5ms interval 100ms——现代 Linux 默认使用 fq_codel(公平队列 + CoDel 主动队列管理,用来对抗缓冲区膨胀),统计行显示这台机器经它发送了 5754309128 bytes 29685823 pktdropped 0。理解这一点对后面很重要:如果直接在 ens5 上加 netem,需要先删掉默认的 fq_codel root qdisc,生产网卡不建议这么干;而回环接口 lo 默认没有生效的排队规则(qdisc noqueue),在上面挂实验性的 netem 最安全。

netem 的模拟只作用于出口方向:包从本机发出时经过 qdisc 被"改造"。回环接口上因为收发都在本机,效果会被放大,这一点稍后实测时会看到。

基线:先量一下"零延迟"的回环

一切模拟都要有参照。先 ping 回环地址,测出本机没有任何额外延迟时的基准:

ping -c 4 127.0.0.1

实测 RTT 在 0.019 ~ 0.045 ms 之间,rtt min/avg/max/mdev = 0.019/0.035/0.045/0.009 ms。记住这个量级:回环本身接近零延迟,后面出现的任何大数字都来自 netem。

注入 100ms 延迟:一条命令让回环"变慢"

现在给 lo 挂上 netem,模拟单程 100ms 的网络延迟:

tc qdisc add dev lo root netem delay 100ms

没有任何输出即成功。用 tc qdisc show 确认它真的挂上了:

tc qdisc show dev lo

tc qdisc show 查看已挂载的 netem

输出 qdisc netem 8001: root refcnt 2 limit 1000 delay 100ms——netem 已作为 lo 的 root qdisc 生效(8001: 是它的句柄,limit 1000 是内部模拟队列上限,refcnt 2 表示被引用中)。

再 ping 一次回环:

ping -c 4 127.0.0.1

ping 回环实测 200ms

实测四个包 RTT 全部是整整齐齐的 200msrtt avg = 200.117 ms,mdev 只有 0.017,稳如磐石)。为什么单程设置 100ms,实测却是 200ms?因为回环接口的包要"出门"一次、"进门"一次,往返各经过一次 qdisc,各吃 100ms。这恰好也是真实网络排查里的常见误区:delay 100ms 指单程延迟,RTT 是两倍——用 iperf3、curl 测出来的都是往返时间。

抖动:真实网络从来不均匀

真实公网的延迟不是恒定的:排队、拥塞会让每个包略有不同。netem 支持给延迟加一个随机偏差:

tc qdisc change dev lo root netem delay 100ms 20ms distribution normal

change 修改已有 qdisc 的参数:100ms 20ms 表示基准 100ms、偏差 ±20ms,distribution normal 让偏差服从正态分布(更贴近真实网络,默认的均匀分布偏差太极端)。再 ping 一组:

ping -c 6 127.0.0.1

实测 6 个包的 RTT 分别是 224、191、211、213、208、204 ms,统计 rtt min/avg/max/mdev = 191.284/208.643/224.413/9.961 ms——mdev(平均偏差)从恒定的 0.017 涨到了 9.961,这就是"抖动"的量化指标。测试播放器、音视频通话这类对延迟敏感的应用时,mdev 往往比平均延迟更致命。

丢包:20% 的丢包率意味着什么

丢包是公网(尤其是跨境链路)最恶劣的杀手,TCP 遇到丢包会触发拥塞控制、指数退避,UDP 则直接丢内容。模拟丢包同样一条命令:

tc qdisc change dev lo root netem delay 100ms loss 20%

设置保持 100ms 延迟,再叠加 20% 丢包。ping 12 个包观察:

ping -c 12 127.0.0.1

ping 实测延迟加丢包

实测 12 个包只收到 7 个(seq 3、4、8、10、12 丢失),41.6667% 丢包率,幸存包的 RTT 仍是稳定的 200ms。注意 20% 的设置为什么实测出 41.7%:loss 20%单程丢包率,而 lo 上每个包往返两级联,只要任一方向被丢就收不到——整段丢包率应为 1-(1-20%)² = 36%,实测 41.7% 与其同量级(12 个包的小样本存在随机波动,丢 5 个在正常范围内)。这也提醒:在真实网络里测试丢包敏感度时,链路两端的丢包是叠加的,模拟时按需调整。

顺带一提,ping 的 time 11115ms 也变长了——每个丢包都要等满 1 秒超时才计入统计,这本身就是"丢包让交互变慢"的直观写照。

清理:让回环恢复原状

实验结束,删掉 lo 上的 netem qdisc:

tc qdisc del dev lo root
tc qdisc show dev lo
ping -c 3 127.0.0.1

实测删除后 tc qdisc show dev lo 输出 qdisc noqueue 0: root refcnt 2——回到初始状态;ping 恢复为 0.024 ~ 0.037 ms(avg 0.032 ms)。tc qdisc del dev lo root 是保底清理手段,重启机器也会自动清空所有 netem 配置。

进阶玩法与真实使用姿势

netem 还能同时注入多种损伤,并叠加带宽限制,一条命令全搞定:

# 乱序与重复(不执行,示例):5% 乱序 + 2% 重复
tc qdisc add dev lo root netem reorder 5% duplicate 2%
# 带宽限制 + 延迟(不执行,示例):限制 1Mbps 同时 50ms 延迟
tc qdisc add dev lo root handle 1: tbf rate 1mbit burst 32kbit latency 400ms
tc qdisc add dev lo parent 1:1 netem delay 50ms

在真实网卡上做模拟时注意三点:一是看清当前 root qdisc(生产网卡一般是 fq_codel),直接 add 会因已存在而报错,需要先 deladd,期间网卡短暂失去队列管理;二是 netem 只影响出方向,想模拟"远端上行差"就把 netem 挂在远端那台机器上;三是模拟完务必删除。更稳妥的日常姿势是把这类实验放在回环接口网络命名空间ip netns + veth)里,与生产流量彻底隔离。

实践中最常用的组合是压测前的"环境降级":在测试服务器上用 netem 注入接近生产的延迟与丢包后,再跑 HTTP 压测:从 ApacheBench 到 wrk 或 iperf3 测吞吐,得到的数据才接近真实公网表现。

小结

netem 的全部玩法浓缩成四条命令:add 挂载、show 查看、change 调参、del 拆除;参数上 delay 100ms 模拟延迟、20ms distribution normal 加抖动、loss 20% 丢包、duplicate/reorder 处理其他损伤。做实验只动回环接口,记住"单程 vs 往返"和"单程丢包 vs 链路总丢包"两个换算关系,就能在本地把全球网络的各种恶劣工况搬进测试环境。更完整的参数说明见 tc-netem 手册页内核文档 Network Emulator

发表评论

暂无评论,快来抢沙发吧!