4. netperf 实战:TCP_STREAM、TCP_RR、UDP_STREAM 与自定义参数
好,咱们进入实操环节。netperf 这工具,我用了快十年了。说实话,它比 iperf 更对我的胃口——不是因为功能多,而是因为它把「连接管理」和「数据传输」拆得清清楚楚。你想想看,做协议栈调优,最怕的就是工具把底层细节藏起来。netperf 不藏,它把每个阶段的耗时都摆在你面前。
这一章,我会带你跑四个最核心的测试场景:TCP_STREAM、TCP_RR、UDP_STREAM,以及怎么用自定义参数把 netperf 玩出花来。每个场景我都会给命令、给解读、给避坑。
核心认知:netperf 的测试模式分为两类——批量传输(STREAM)和 请求-响应(RR)。前者看吞吐上限,后者看事务处理能力。调优时,这两类数据缺一不可。
4.1 TCP_STREAM:测透 TCP 的极限吞吐
TCP_STREAM 是 netperf 最基础的测试。它模拟的是「一条 TCP 连接,发送端拼命发,接收端拼命收」的场景。说白了,就是看你的网络栈在理想条件下能跑多快。
先看命令。我习惯把服务端挂在后台,用 -D 参数让它不 fork 子进程,减少上下文切换的干扰:
# 服务端(被测试机)
netserver -D
# 客户端(测试发起机)
netperf -H 192.168.1.100 -l 60 -t TCP_STREAM
参数解释:
-H:指定服务端 IP-l 60:测试持续 60 秒-t TCP_STREAM:指定测试类型
输出长这样:
TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.1.100 (192.168.1.100) port 0 AF_INET
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec
87380 16384 16384 60.00 941.23
关键看最后一列:Throughput,单位是 Mbps。941.23 Mbps,接近千兆线速。如果这个值远低于链路带宽,那你的协议栈肯定有瓶颈。
我的习惯:TCP_STREAM 测试至少跑 3 次,取中位数。单次结果受 CPU 调度、中断亲和性影响很大。我曾经在同一个机器上连续跑 5 次,结果从 920 跳到 980 又掉回 910——后来发现是 irqbalance 在捣乱。
4.2 TCP_RR:事务处理能力的照妖镜
TCP_RR 测试的是「请求-响应」模式。客户端发一个请求,服务端回一个响应,算一次事务(Round Trip)。这个测试对延迟极其敏感——你想想看,如果一次往返要 1 毫秒,那理论最大事务率就是 1000 TPS。实际值低于这个数,说明协议栈在处理小包时有问题。
命令:
netperf -H 192.168.1.100 -l 60 -t TCP_RR
输出示例:
TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.1.100 (192.168.1.100) port 0 AF_INET
Local /Remote
Socket Size Request Resp. Elapsed Trans.
Send Recv Size Size Time Rate
bytes Bytes bytes bytes secs. per sec
16384 87380 1 1 60.00 4521.34
关键列:Trans. Rate,单位是 trans/s(事务每秒)。4521 TPS,意味着每次往返约 221 微秒。如果这个值低于预期,我通常会先查两个东西:
- NAPI 的 gro_flush_timeout:默认值可能把小包攒太久了
- 中断合并(coalescing):ethtool -C 里的 rx-usecs 设太大,延迟会飙升
我曾经踩过的坑:有一次 TCP_RR 只有 800 TPS,链路延迟明明只有 0.3ms。查了两天,最后发现是服务端开了 tcp_nagle,请求被延迟发送了。关掉之后直接飙到 4200 TPS。所以测 RR 之前,记得检查 TCP_NODELAY 是否生效。
4.3 UDP_STREAM:无连接,但有坑
UDP_STREAM 测试的是 UDP 的发送吞吐。注意,netperf 的 UDP_STREAM 只统计发送端的数据——接收端丢了多少包,它不管。你需要配合 -k 参数看丢包统计。
命令:
netperf -H 192.168.1.100 -l 60 -t UDP_STREAM -- -m 1472
-m 1472 指定 UDP 载荷大小。1472 是标准 MTU 1500 减去 IP 头 20 和 UDP 头 8 后的最大值。用这个值可以避免 IP 分片。
输出:
UDP UNIDIRECTIONAL SEND TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.1.100 (192.168.1.100) port 0 AF_INET
Socket Message Elapsed Messages
Size Size Time Okay Errors Throughput
bytes bytes secs # # 10^6bits/sec
65536 1472 60.00 812345 0 159.42
看 Throughput 和 Errors。Errors 为 0 说明发送端没报错,但不代表接收端都收到了。要确认接收情况,得在服务端用 netstat -s 看 UDP 丢包计数。
关键认知:UDP_STREAM 测的是「发送能力」,不是「传输质量」。如果接收端丢包严重,你需要关注的是接收缓冲区大小(rmem_max)和 GRO 的收包效率。
4.4 自定义参数:把 netperf 变成你的瑞士军刀
默认参数只能测个大概。真正做调优时,你得能控制每一个变量。netperf 的自定义参数通过 -- 后面的选项传入,我列几个最常用的:
| 参数 | 作用 | 典型值 | 我的用法 |
|---|---|---|---|
-m |
设置发送消息大小(字节) | 1, 64, 1024, 65536 | 测小包性能时用 1 或 64 |
-M |
设置接收消息大小(字节) | 同 -m | TCP_RR 中控制请求/响应大小 |
-s |
设置本地 Socket 缓冲区大小 | 87380, 262144 | 验证缓冲区对吞吐的影响 |
-S |
设置远端 Socket 缓冲区大小 | 同 -s | 配合 -s 一起调 |
-D |
启用 TCP_NODELAY | 无参数 | 测 RR 时必加 |
-k |
指定输出哪些统计字段 | THROUGHPUT, P50_LATENCY | 获取延迟分布数据 |
举个例子,我想测「64 字节小包、启用 TCP_NODELAY、持续 120 秒」的 TCP_RR:
netperf -H 192.168.1.100 -l 120 -t TCP_RR -D -- -m 64 -M 64 -k THROUGHPUT,MIN_LATENCY,MAX_LATENCY,P50_LATENCY,P99_LATENCY
输出会多出延迟分布信息:
THROUGHPUT=4521.34
MIN_LATENCY=187
MAX_LATENCY=2341
P50_LATENCY=221
P99_LATENCY=389
看到 P99 比 P50 高了近一倍?这说明有尾部延迟问题。我一般会接着查中断亲和性、CPU 隔离、或者网卡的 RSS 哈希是否均匀。
我的经验:自定义参数测试一定要做「变量控制」。一次只改一个参数,记录结果。我习惯用脚本批量跑,输出 CSV 格式,然后用 Python 画趋势图。比如「消息大小 vs 吞吐量」这条曲线,能直接告诉你协议栈在小包场景下的瓶颈在哪。
4.5 实战组合:一次完整的调优测试流程
最后,我分享一个我常用的测试流程。每次调优前后,我都会跑这组测试,确保改动没有引入副作用:
- 基线 TCP_STREAM:
-m 65536,测大包吞吐 - 小包 TCP_STREAM:
-m 64,测小包吞吐 - TCP_RR 默认:1 字节请求/响应,测事务率
- TCP_RR 大载荷:
-m 4096 -M 4096,测大载荷下的响应能力 - UDP_STREAM:
-m 1472,测 UDP 发送能力 - UDP_RR:
-m 64 -M 64,测 UDP 事务率
这六组数据跑完,你的网络栈是什么水平,瓶颈在哪,基本就清楚了。剩下的就是针对性地调参数、测回归、再调参数——循环直到达标。
注意:测试过程中,一定要用 perf top 或 mpstat -P ALL 1 监控 CPU 使用情况。如果某个 CPU 核心跑到 100%,而其他核心空闲,说明中断亲和性或 RFS/RPS 配置有问题。我曾经遇到过 TCP_STREAM 吞吐上不去,结果发现所有中断都打在一个核心上——调整 smp_affinity 后直接提升 30%。