欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux性能优化-网络性能评估

程序员文章站 2022-05-16 22:37:41
...

 

目录

网络基准测试

转发性能

TCP/UDP性能

HTTP性能

应用负载性能

参考


在Linux中常见的网络性能指标如下

  1. 带宽,表示链路的最大传输速率,单位是b/s 比特/秒,在位服务器选网卡时,带宽就是最核心的参考指标,常用的带宽有1000M,10G,40G,100G等
  2. 吞吐量,表示没有丢包时最大的数据传输速率,单位通常是b/s比特/秒,或B/s字节/秒,吞吐量受带宽的限制,吞吐量/带宽也是该网络链路的使用率
  3. 延迟,表示从网络请求发出后,一直到收到远端响应所需要的时间延迟,这个指标在不同场景中有不同含义,它可以表示建立连接需要的时间(TCP握手延时),或者一个数据包往返所需时间RTT
  4. PPS,是Packet Per Second包/秒 缩写,表示已网络包围单位的传输速率,PPS通常用来评估网络的转发能力,而基于Linux服务器的转发,很容易受到网络包大小的影响(交换机通常不会受太大影响,即交换机可以线性转发)

这四个指标中,贷款跟屋里网卡配置是直接关联的,一般来说,网卡确定后,带宽也就确定了(实际贷款会受限于整个网络链路中最小的那个模块)
另外,网络带宽测试,这里测试的不是带宽,而是网络吞吐量,Linux服务器的网络吞吐量一般会比带宽小,而对交换机等专门的网络设备来说,吞吐量一般会接近贷款

最后的PPS,则是以网络包围单位的网络传输速率,通常用在需要大量转发的场景中,而对TCP或者Web服务器来说,更多回用并发连接数和每秒请求数(QPS Query per Second)等指标,他们更能反映实际应用程序的性能

 

网络基准测试

Linux网络基于TCP/IP协议栈,而不同协议层的行为显然不同,在测试之前,需要评估网络性能,究竟是属于协议栈那一层,也就是你的应用程序基于协议的那一层

根据TCP/IP协议栈原理
基于HTTP或HTTPS的Web应用程序,属于应用层,需要我们测试HTTP/HTTPS的性能
大多数游戏服务器,为了支持更大的同时在线人数,通常会基于TCP或UDP,与客户端交互,这时就需要测试TCP/UDP的性能
还有一些场景,把Linux作为一个软交换机或路由器来用,这种情况下需要关注网络包的处理能力PPS,重点关注网络层的转发性能

需要注意,低层协议是其上的各层网络协议的基础,低层协议的性能,也就决定了高层网络性能
各协议层的性能测试

转发性能

网络接口层和网络层,注意负责网络包的封装,寻址,路由,转发和接收,在这两个网络协议层中,每秒可以处理的网络包数PPS,就是最终于的性能指标,特别是64B小包的处理能力,值得我们特别关注

软件断案例中的hping3就是测试网络包处理能力的性能工具
还有更常用的工具,Linux内核自带的高性能网络测试工具 pktgen,支持更丰富的自定义选项,可以更准确的测试出目标服务器的性能

在Linux中部能直接使用 pktgen命令,因为pktgen作为一个内核线程来运行的,需要加载pktgen内核模块后,再通过/proc文件系统来交互

下面是pktgen启动的四个内核线程和/proc文件系统的交互文件

modprobe pktgen

ps -ef | grep pktgen | grep -v grep
root     20211     2  0 10:18 ?        00:00:13 [kpktgend_0]
root     20212     2  0 10:18 ?        00:00:05 [kpktgend_1]
root     20213     2  0 10:18 ?        00:00:05 [kpktgend_2]
root     20214     2  0 10:18 ?        00:00:05 [kpktgend_3]

ls /proc/net/pktgen/
eth0  kpktgend_0  kpktgend_1  kpktgend_2  kpktgend_3  pgctrl

pktgen在每个CPU上启动一个内核线程,并可以通过/proc/net/pktgen 下面的同名文件,跟这些线程交互,而pgctrl则主要用来控制这次测试的开启和停止

如果modprobe命令执行失败,说明内核没有配置
CONFIG_NET_PKTGEN 选项,这就需要配置pktgen内核模块(即CONFIG_NET_PKTGEN=m)后,重新编译内核,才可以使用
在使用pktgen测试网络性能时,需要先给每个内核线程kpktgend_X以及测试网卡,配置pktgen选项,然后再通过pgctrl启动测试
 

定义一个测试脚本 test.sh

# 定义一个工具函数,方便后面配置各种测试选项
function pgset() {
    local result
    echo $1 > $PGDEV

    result=`cat $PGDEV | fgrep "Result: OK:"`
    if [ "$result" = "" ]; then
         cat $PGDEV | fgrep Result:
    fi
}

# 为 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 192.168.0.30" # 目的 IP
pgset "dst_mac 11:11:11:11:11:11"  # 目的 MAC

# 启动测试
PGDEV=/proc/net/pktgen/pgctrl
pgset "start"

执行测试脚本后的结果

#执行test.sh脚本后结果
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: 【目标端机器IP】  dst_max: 
        src_min:   src_max: 
     src_mac: 【源端MAC地址】 dst_mac: 【目标端MAC地址】
     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: 4505804346082us  stopped: 4505811484273us idle: 3929112us
     seq_num: 1000001  cur_dst_mac_offset: 0  cur_src_mac_offset: 0
     cur_saddr: 0x8b6e680a  cur_daddr: 0x8a6e680a
     cur_udp_dst: 9  cur_udp_src: 9
     cur_queue_map: 0
     flows: 0
Result: OK: 7138191(c3209079+d3929112) nsec, 1000000 (64byte,0frags)
  140091pps 71Mb/sec (71726592bps) errors: 0

可以看到测试报告主要分为三个部分

  • 第一部分 Params是测试选项
  • 第二部分 Current是测试进度,其中packts so far(pkts-sofar)表示已经发送了100W个包,也就表明测试完成
  • 第三部分 Result是测试结果,包含测试所用的时间,网络包数量和分片,PPS,吞吐量,错误数

根据上面结果可以看到,PPS为14W,吞吐量为71Mb/s,没有发生错误

作为对比,千兆交换机的PPS,交换机可以达到线性速度(满负载时无差错转发),它的PPS就是1000Mbit除以以太网帧大小,即1000Mbps/((64+20)*8bit)=1.5Mpps,其中20B位以太网帧的头部大小

即便是千兆交换机的PPS,也可以达到150W PPS,比测试的14W大很多,现在多喝服务器和万兆网卡已经很普遍,稍作优化就可以达到百万PPS,而且用了DPDK和XDP,还能打到千万数量级

 


TCP/UDP性能

再来看看TCP和UDP的性能测试方法
云计算时代,当拿到一批虚拟机时,首先要做的是,应该用iperf测试网络性能是否符合预期
常用的有iperf和netperf,测试TCP和UDP的吞吐量,他们都以客户端和服务器通讯方式,测试一段时间内平均吞吐量
例子如下

安装iperf3,默认的iperf机器上都有了,这里安装最新的

# 安装最新的iperf3
yum install iperf3

服务端启动,收到的结果如下

----------------------------
Server listening on 10000
-----------------------------------------------------------
Accepted connection from 【客户端】, port 54128
[  5] local 【服务端】 port 10000 connected to 【客户端】 port 54130
[  7] local 【服务端】 port 10000 connected to 【客户端】 port 54132
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-1.00   sec  51.6 MBytes   432 Mbits/sec                  
[  7]   0.00-1.00   sec  46.5 MBytes   390 Mbits/sec                  
[SUM]   0.00-1.00   sec  98.0 MBytes   822 Mbits/sec                  
。。。
[  5]  10.00-10.08  sec  4.87 MBytes   507 Mbits/sec                  
[  7]  10.00-10.08  sec  4.24 MBytes   441 Mbits/sec                  
[SUM]  10.00-10.08  sec  9.11 MBytes   948 Mbits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-10.08  sec  0.00 Bytes  0.00 bits/sec                  sender
[  5]   0.00-10.08  sec   566 MBytes   471 Mbits/sec                  receiver
[  7]   0.00-10.08  sec  0.00 Bytes  0.00 bits/sec                  sender
[  7]   0.00-10.08  sec   559 MBytes   465 Mbits/sec                  receiver
[SUM]   0.00-10.08  sec  0.00 Bytes  0.00 bits/sec                  sender
[SUM]   0.00-10.08  sec  1.10 GBytes   936 Mbits/sec                  receiver
-----------------------------------------------------------
Server listening on 10000
-----------------------------------------------------------

客户端启动后执行结果如下

iperf3 -c 【服务端】 -b 1G 15 -P 2 -p 10000
Connecting to host 【服务端】, port 10000
[  4] local 【客户端】 port 54130 connected to 【服务端】 port 10000
[  6] local 【客户端】 port 54132 connected to 【服务端】 port 10000
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec  55.7 MBytes   467 Mbits/sec    0   2.56 MBytes       
[  6]   0.00-1.00   sec  51.3 MBytes   431 Mbits/sec    0   2.35 MBytes       
[SUM]   0.00-1.00   sec   107 MBytes   898 Mbits/sec    0             
- - - - - - - - - - - - - - - - - - - - - - - - -
。。。        
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec   566 MBytes   475 Mbits/sec    0             sender
[  4]   0.00-10.00  sec   566 MBytes   475 Mbits/sec                  receiver
[  6]   0.00-10.00  sec   559 MBytes   469 Mbits/sec    0             sender
[  6]   0.00-10.00  sec   559 MBytes   469 Mbits/sec                  receiver
[SUM]   0.00-10.00  sec  1.10 GBytes   944 Mbits/sec    0             sender
[SUM]   0.00-10.00  sec  1.10 GBytes   944 Mbits/sec                  receiver

看服务端结果,最后SUM就是测试的汇总结果,包括测试时间,数据传输量,带宽等
按照发送和接收,这一部分又分为sender和receiver两行
从测试结果看,这台机器的TCP接收的带宽(吞吐量)为936Mb/s


HTTP性能

从传输层再往上,到了应用层,有的应用程序会直接基于TCP或UDP构建服务,但也有大量应用基于应用层协议来构建服务,HTTP就是最常见的应用层协议,如Apache,Nginx等各种Web服务器
测试HTTP的性能有ab,webbench等
下面以ab为例,主要测试HTTP服务的每秒请求数,请求延迟,吞吐量,请求延迟分布等情况

# 安装 ab工具
yum install -y httpd-tools

#在目标机器上使用Docker启动Nginx服务
docker run -p 10000:80 -itd nginx


# 用ab测试
# -c表示并发请求数为1000, -n表示总的请求数为10000
ab -c 1000 -n 10000 http://localhost:10000/ 
Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        nginx/1.15.7
Server Hostname:        localhost
Server Port:            10000

Document Path:          /
Document Length:        612 bytes

Concurrency Level:      1000
Time taken for tests:   6.087 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      8450000 bytes
HTML transferred:       6120000 bytes
Requests per second:    1642.80 [#/sec] (mean)
Time per request:       608.716 [ms] (mean)
Time per request:       0.609 [ms] (mean, across all concurrent requests)
Transfer rate:          1355.63 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0  222 424.5      1    3112
Processing:    23  243 348.3    157    3516
Waiting:        1  241 348.4    156    3516
Total:         36  465 636.7    171    4525

Percentage of the requests served within a certain time (ms)
  50%    171
  66%    224
  75%    302
  80%   1139
  90%   1252
  95%   1695
  98%   2104
  99%   2161
 100%   4525 (longest request)


可以看到,ab的测试结果分为三个部分,分别是请求汇总,连接时间汇总,请求延迟汇总
在请求汇总部分可以看到

  • Requests per second:    1642.80 [#/sec] (mean)
  • Time per request(每个请求延迟,这里是平均延迟,包括了线程运行的调度时间和网络请求响应时间):       608.716 [ms] (mean)
  • Time per request(表示实际请求的响应时间):       0.609 [ms] (mean, across all concurrent requests)
  • Transfer rate(实际吞吐量):          1355.63 [Kbytes/sec] received

连接时间汇总部分,则是分别展示了建立连接,请求,等待以及汇总等各类时间,包括最小,最大,平均以及中值处理时间
最后的请求延迟汇总部分,则给出了不同时间段内处理请求的百分比,如90%的请求都在1252ms内完成
 


应用负载性能

当用iperf或ab等测试工具,得到TCP,HTTP等性能数据后,这是数据并不能表示应用程序的实际性能
如应用程序基于HTTP协议,为最终用户提供了一个Web服务,这是使用ab工具,可以得到某个页面的访问性能,但这个结果跟用户的实际请求,很可能不一致,因为用户请求往往会附带各种负载(payload),而这些负载会影响Web应用程序内部的处理逻辑,从而影响最终性能
为了得到应用程序的实际性能,就要求性能工具本身可以模拟用户的请求负载,而iperf,ab就无能为力了,不过还可以用wrk,TCPCopy,Jmeter或者LoadRunner等实现这个目标

以wrk为例,他是一个HTTP性能测试工具,内置了LuaJIT,方便根据实际需求,生成所需的请求负载,或自定义响应处理方法
wrk工具需要源码安装

wget https://github.com/wg/wrk .
cd wrk
yum install -y build-essential
make

执行命令如下

# -c 表示并发连接数为1000 -c 表示线程数为2
wrk -c 1000 -t 2 http://localhost:10000
Running 10s test @ http://localhost:10000
  2 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   138.52ms  116.97ms   1.97s    92.43%
    Req/Sec     2.89k   833.60     4.88k    80.77%
  54105 requests in 10.05s, 43.86MB read
  Socket errors: connect 0, read 0, write 0, timeout 73
Requests/sec:   5385.71
Transfer/sec:      4.37MB

这里使用2个线程,并发1000连接,重新测试了Nginx的性能,每秒的请求数为5385,吞吐量为4.37MB,平均延迟为138ms,比前面ab的测试结果要好
这说明性能工具本身,对性能测试也是至关重要的,不适合的性能工具,并不能准确测试出应用程序的最佳性能
wrk的最大优势,是其内置的LuaJIT,可以用来实现复杂场景的性能测试,wrk在调用Lua脚本时,可以将HTTP请求分为三个阶段,即setup,running,done,如下图所示

Linux性能优化-网络性能评估

增加一段自定义的Lua脚本

token = nil
path  = "/authenticate"

request = function()
   return wrk.format("GET", path)
end

response = function(status, headers, body)
   if not token and status == 200 then
      token = headers["X-Token"]
      path  = "/resource"
      wrk.headers["X-Token"] = token
   end
end

新的执行过程如下

# -s 选项加入脚本
wrk -c 1000 -t 2 -s auth.lua http://localhost:10000
Running 10s test @ http://localhost:10000
  2 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   137.97ms   40.71ms   1.96s    91.36%
    Req/Sec     1.62k   441.57     3.13k    68.56%
  31999 requests in 10.02s, 9.40MB read
  Socket errors: connect 0, read 0, write 0, timeout 89
  Non-2xx or 3xx responses: 31999
Requests/sec:   3193.01
Transfer/sec:      0.94MB

wrk需要使用Lua脚本,来构造请求负载,对于大部分场景来说,已经足够了,不过这也是其缺点,所有东西都需要代码来构造,并且工具本身不提供GUI环境
像Jmeter或者LoadRunner(商业),则针对复杂场景提供了脚本录制,回放,GUI等更丰富的功能
 

 

 

 

参考

wrk