目录
网络基准测试
转发性能
TCP/UDP性能
HTTP性能
应用负载性能
参考
在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
可以看到测试报告主要分为三个部分
根据上面结果可以看到,PPS为14W,吞吐量为71Mb/s,没有发生错误
作为对比,千兆交换机的PPS,交换机可以达到线性速度(满负载时无差错转发),它的PPS就是1000Mbit除以以太网帧大小,即1000Mbps/((64+20)*8bit)=1.5Mpps,其中20B位以太网帧的头部大小
即便是千兆交换机的PPS,也可以达到150W PPS,比测试的14W大很多,现在多喝服务器和万兆网卡已经很普遍,稍作优化就可以达到百万PPS,而且用了DPDK和XDP,还能打到千万数量级
再来看看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
从传输层再往上,到了应用层,有的应用程序会直接基于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的测试结果分为三个部分,分别是请求汇总,连接时间汇总,请求延迟汇总
在请求汇总部分可以看到
连接时间汇总部分,则是分别展示了建立连接,请求,等待以及汇总等各类时间,包括最小,最大,平均以及中值处理时间
最后的请求延迟汇总部分,则给出了不同时间段内处理请求的百分比,如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,如下图所示
增加一段自定义的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