Linux 网络性能分析
Jun 2, 2020 23:00 · 2072 words · 5 minute read
衡量网络性能的几个指标
- 带宽(b/s):表示链路的最大传输速度。
- 吞吐量(b/s):表示没有丢包时的最大数据传输速度,受带宽的限制,吞吐量/带宽也就是该网络链路的使用率。
- 延迟:请求发出后直到收到应答所需的时间。
- PPS(Packet Per Second):以网络包为单位的传输速度,通常用于评估网络设备(交换机)的转发能力。
Linux 服务器的网络吞吐量一般比带宽小,交换机等专业的网络设备的吞吐量可以接近带宽。 对于基于 TCP 的 web 服务,更多用每秒请求数 QPS(Query Per Second)等指标。
网络基准测试
在测试前要弄清楚你想要评估的网络性能属于 TCP/IP 协议栈的哪一层?
- 基于 HTTP 的 web 应用程序属于应用层
- 大多数游戏和物联网应用程序服务通常会基于 TCP 或 UDP
- 软路由等设备把 Linux 作为路由器来使用
不同的测试场景有不同的测试指标与测试工具。
转发性能(PPS)
网络接口层和网络层主要负责网络包的封装、寻址、路由、发送和接收,在这两层中,每秒可处理的网络包数 PPS 是最重要的性能指标。
这里我们使用 Linux 内核自带的高性能网络测试工具 pktgen。
在 Linux 系统中并不能显式地使用 pktgen 命令,因为 pktgen 作为一个内核线程来运行,需要加载 pktgen 内核模块后再通过 /proc 文件系统来交互:
$ modprobe pktgen
$ ps -ef | grep pktgen
root 10686 2 0 21:22 ? 00:00:00 [kpktgend_0]
root 10688 2 0 21:22 ? 00:00:00 [kpktgend_1]
root 10689 2 0 21:22 ? 00:00:00 [kpktgend_0]
root 10690 2 0 21:22 ? 00:00:00 [kpktgend_1]
root 10691 2 0 21:22 ? 00:00:00 [kpktgend_0]
root 10692 2 0 21:22 ? 00:00:00 [kpktgend_1]
$ ll /proc/net/pktgen
total 0
-rw-------. 1 root root 0 Jun 2 21:37 kpktgend_0
-rw-------. 1 root root 0 Jun 2 21:37 kpktgend_1
-rw-------. 1 root root 0 Jun 2 21:37 pgctrl
pktgen 在每个 CPU 上启动一个内核线程,通过 /proc/net/pktgen 路径下的同名文件与之交互。pgctrl 工具用来控制测试的开启与停止。
function pgset() {
local result
echo $1 > $PGDEV
result=`cat $PGDEV | fgrep "Result: OK:"`
if [ "$result" = "" ]; then
cat $PGDEV | fgrep Result:
fi
}
将以上工具脚本函数粘贴到 shell,为 0 号线程绑定 eth0 网卡:
$ PGDEV=/proc/net/pktgen/kpktgend_0
$ pgset "rem_device_all" # 清空网卡绑定
$ pgset "add_device eth0" # 添加eth0网卡
配置 eth0 网卡的测试选项:
$ PGDEV=/proc/net/pktgen/eth0
$ pgset "count 1000000" # 总发包数量
$ pgset "delay 5000" # 不同包之间的发送延迟(单位纳秒)
$ pgset "clone_skb 0" # SKB包复制
$ pgset "pkt_size 64" # 网络包大小
$ pgset "dst 10.211.55.104" # 目的IP
$ pgset "dst_mac 00:1c:42:82:59:fa" # 目的MAC
启动测试:
$ PGDEV=/proc/net/pktgen/pgctrl
$ pgset "start"
等测试完成后,从 /proc 文件系统中提取结果:
cat /proc/net/pktgen/eth0
Params: count 1000000 min_pkt_size: 64 max_pkt_size: 64
frags: 0 delay: 5000 clone_skb: 0 ifname: eth0
flows: 0 flowlen: 0
queue_map_min: 0 queue_map_max: 0
dst_min: 10.211.55.104 dst_max:
src_min: src_max:
src_mac: 00:1c:42:c5:e3:d0 dst_mac: 00:1c:42:82:59:fa
udp_src_min: 9 udp_src_max: 9 udp_dst_min: 9 udp_dst_max: 9
src_mac_count: 0 dst_mac_count: 0
Flags:
Current:
pkts-sofar: 1000000 errors: 0
started: 1910481740us stopped: 1920943338us idle: 9424998us
seq_num: 1000001 cur_dst_mac_offset: 0 cur_src_mac_offset: 0
cur_saddr: 10.211.55.105 cur_daddr: 10.211.55.104
cur_udp_dst: 9 cur_udp_src: 9
cur_queue_map: 0
flows: 0
Result: OK: 10461598(c1036600+d9424998) usec, 1000000 (64byte,0frags)
95587pps 48Mb/sec (48940544bps) errors: 0
- Params 段为测试参数
- Current 段为测试进度,当前已完成
- Result 段为测试结果:测试用时 10461598 纳秒;总共 1000000 64 字节包;每秒 95587 包;吞吐量 48Mb/s;0 错误。
对比千兆交换机可以达到 150W PPS,10W 不到可以说是性能极差。
TCP/UDP 性能
iperf3 和 netperf 是最常用的网络性能测试工具之一,用于测试 TCP/UDP 的吞吐量。
在目标服务器上启动 iperf3 服务器:
$ iperf3 -s -i 1 -p 8080
-----------------------------------------------------------
Server listening on 8080
-----------------------------------------------------------
-s
启动服务端-i 1
汇报间隔 1 秒-p 8080
监听 8080 端口
用 iperf3 客户端对目标服务器发包测试:
$ iperf3 -c 10.211.55.104 -b 1G -t 15 -P 2 -p 8080
-c 10.211.55.104
启动客户端向目标 IP 10.211.55.104 发包-b 1G
目标带宽 1Gb/s-t 15
持续传输 15 秒-P 2
两个客户端并发-p 8080
目标端口 8080
测试完成后回到目标服务器上:
[ 5] 15.00-15.04 sec 0.00 Bytes 0.00 bits/sec
[ 7] 15.00-15.04 sec 0.00 Bytes 0.00 bits/sec
[SUM] 15.00-15.04 sec 0.00 Bytes 0.00 bits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-15.04 sec 0.00 Bytes 0.00 bits/sec sender
[ 5] 0.00-15.04 sec 1.74 GBytes 992 Mbits/sec receiver
[ 7] 0.00-15.04 sec 0.00 Bytes 0.00 bits/sec sender
[ 7] 0.00-15.04 sec 1.74 GBytes 992 Mbits/sec receiver
[SUM] 0.00-15.04 sec 0.00 Bytes 0.00 bits/sec sender
[SUM] 0.00-15.04 sec 3.47 GBytes 1.98 Gbits/sec receiver
SUM 行为测试的汇总结果,测试时间 15 秒;传输了 3.47 GBytes 的数据量;这台服务器 TCP 接收的带宽/吞吐量为 1.98 Gb/s,远超测试目标 1Gb。
HTTP Web 应用程序性能
传输层往上就是应用层。ab、siege、webbench 都是常用的 HTTP 压力测试工具,可以用于测试 HTTP 服务的每秒请求数、请求延迟、吞吐量以及请求延迟的分布情况等。
首先在目标服务器上运行一个 nginx Docker 容器:
$ docker run --rm -p 80:80 -d nginx
在本地运行 ab 工具:
$ ab -c 1000 -n 10000 http://10.211.55.104/
Benchmarking 10.211.55.104 (be patient)
Completed 1000 requests
...
Finished 10000 requests
Server Software: nginx/1.19.0
Server Hostname: 10.211.55.104
Server Port: 80
Document Path: /
Document Length: 612 bytes
Concurrency Level: 1000
Time taken for tests: 0.685 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 8450000 bytes
HTML transferred: 6120000 bytes
Requests per second: 14591.53 [#/sec] (mean)
Time per request: 68.533 [ms] (mean)
Time per request: 0.069 [ms] (mean, across all concurrent requests)
Transfer rate: 12040.86 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 23 6.2 22 42
Processing: 8 34 10.2 33 59
Waiting: 0 24 8.4 25 53
Total: 33 56 9.6 55 84
Percentage of the requests served within a certain time (ms)
50% 55
66% 59
75% 64
80% 65
90% 70
95% 72
98% 75
99% 77
100% 84 (longest request)
- 请求汇总
- Requests per second 每秒请求数(RPS)为 14591.53
- 平均延迟 68.533 ms,这个包括了线程运行的调度时间和网络请求响应时间
- 实际请求的响应时间为 0.069 ms
- Transfer rate 也就是吞吐量为 12040 KB/s
- 连接时间汇总
- 建立连接
- 请求
- 等待
- 请求延迟汇总部分,90% 的请求都在 70 ms 内完成。
ab 测试得到的 HTTP 性能数据并不能代表应用程序的真实性能,用户的请求往往附带着各种负载,到达服务器应用程序后触发业务逻辑(数据库读写),这种情况我们可以使用 wrk 更贴切地模拟真实场景。
$ brew install wrk
$ wrk -c 1000 -t 2 http://10.211.55.104
Running 10s test @ http://10.211.55.104
2 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.20ms 3.88ms 78.02ms 91.57%
Req/Sec 30.32k 2.48k 36.41k 69.31%
609494 requests in 10.10s, 494.04MB read
Socket errors: connect 749, read 0, write 0, timeout 0
Requests/sec: 60318.96
Transfer/sec: 48.89MB
-c 1000
并发连接数 1000-t 2
启动两条线程
用 wrk 重新测试得到的 RPS 为 6W 多,吞吐量为 49MB/s,比 ab 测试出来的结果好得多。不合适的性能工具,并不能准确测出应用程序的最佳性能。
wrk 最大的优势在于编写 Lua 脚本实现复杂测试场景的能力,比如带参数的 POST 请求:
$ cat <<EOF > post.lua
wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
EOF
$ wrk -c 1000 -t 2 -s post.lua
wrk 官方也提供了 demo 脚本来应对一些简单的应用场景 https://github.com/wg/wrk/tree/master/scripts。