Linux 网络性能分析

Jun 2, 2020 23:00 · 2072 words · 5 minute read Linux Network Performance

衡量网络性能的几个指标

  • 带宽(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