背景
最近数据中心SDN项目有一个测试需求,要测试OVS-DPDK百万TCP连接支持情况。OVS使用有状态ACL时,它会感知和跟踪TCP连接,所以有测试最大TCP连接数目的需求。
jmeter每个htttp user是一个线程。Locust每个http user是一个协程。所以相同的资源条件下locust能模拟的http user数目远超jmeter。
Locust基于使用epoll的libev来实现socket的IO复用,基于greenlet实现高效的协程切换。
安装
locust是基于python开发的,所以首先安装python。然后pip install locust安装locust。
本次测试版本信息
vm linux kernel 3.10,nginx 1.20.1,python 3.8.10,locust 2.13.1。
测试设计
locust做http客户端,nginx做http服务端。
测试拓扑:
locust-(vm)-OVS-DPDK-1-(server1)-------OVS-DPDK-2-(server2)-nginx
server1上,创建17台4核8G的vm。1台vm运行locust的master,16台vm运行locust的worker。
server2上,一台8核16G的vm运行nginx。
locust master只负责下发任务给locust worker,接受统计和展示,并不向nginx发起连接。
每个locust worker建立62500条TCP连接(TCP端口空间是64k),16台worker vm共计发起100万条连接。
环境配置
为了减少数据通信量,修改nginx vm上/usr/share/nginx/html/index.html的内容为:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Welcome to CentOS</title>
</head>
<body>
</body>
</html>
共128字节长。
为了增加支持的连接数和让nginx保持持久连接,修改nginx配置文件/etc/nginx/nginx.conf的内容为:
user root;
worker_processes auto;
worker_rlimit_nofile 1024000;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
use epoll;
worker_connections 1024000;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 1024000;
keepalive_requests 1024000;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
server {
listen 8080;
listen [::]:8080;
server_name _;
root /usr/share/nginx/html;
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
locust的master和worker vm上都创建locustfile.py,内容为:
from locust import HttpUser, task, constant
class HelloWorldUser(HttpUser):
wait_time = constant(900)
@task(1)
def get_index(self):
r = self.client.get("/")
主要动作是每个user每隔900秒发送GET请求主页。因为并没有调用r.connection.close()来关闭连接,所以locust保持长连接。
nginx vm和locust worker vm上修改协议栈相关参数。主要是为了增加支持的最大连接数,同时捎带增加了新建连接能力、数据收发能力。
sysctl -w net.ipv4.tcp_timestamps=0
sysctl -w net.ipv4.ip_local_port_range='1024 64000'
sysctl -w net.core.somaxconn=32768
sysctl -w net.ipv4.tcp_max_syn_backlog=262144
sysctl -w net.ipv4.tcp_syncookies=0
sysctl -w net.ipv4.tcp_max_orphans=262144
sysctl -w net.ipv4.tcp_max_tw_buckets=5000
sysctl -w fs.file-max=10485760
ulimit -n 1024000
ulimit -u 2048000
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
sysctl -w net.ipv4.tcp_rmem='32768 131072 16777216'
sysctl -w net.ipv4.tcp_wmem='32768 131072 16777216'
sysctl -w net.core.netdev_max_backlog=262144
nginx vm上重启nginx服务:
systemctl daemon-reload
systemctl restart nginx
如果nginx worker能够使用的文件描述符仍然是缺省的1024个,则创建文件/etc/systemd/system/nginx.service.d/override.conf,写入内容:
[Service]
LimitNOFILE=1024000
LimitNOFILESoft=1024000
然后重启nginx服务。
测试实施
server1 vm0上,运行locust master,等待woker数目达到16个时,一起开始建立连接,总共模拟100万http用户,spawn rate是1600user/second。即每个worker模拟62500个用户,每个worker的spawn rate是100user/second,所以大约625秒后,连接总数达到100万。
这里nginx vm的IP地址是192.168.102.2。
locust --headless -u 1000000 -r 1600 -H http://192.168.102.2:8080 --master --expect-workers 16
server2 vm1-vm16上,运行locust worker,选项master-host指定locust master vm的地址。
locust --worker --master-host=192.168.101.6&
测试结果
开始加压625秒后,连接总数达到100万。然后保持连接。
locust master vm上可以看到输出
Type Name # reqs # fails | Avg Min Max Med | req/s failures/s
--------||-------|-------------|-------|-------|-------|-------|--------|-----------
GET / 1000000 0(0.00%) | 90 30 3411 74 | 1594.50 0.00
--------||-------|-------------|-------|-------|-------|-------|--------|-----------
Aggregated 1000000 0(0.00%) | 90 30 3411 74 | 1594.50 0.00
100万请求全部成功。
ngxin vm统计连接数:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 1000000
建立了100万条连接。
开始加压900秒后,开始第二轮HTTP request的发送。第二轮request仍然使用前面建立的连接。
根据RFC3511 5.2 Concurrent TCP Connection Capacity的要求,应该在所有连接建立完成后,再发送一轮http GET,用于确认所有连接都建立后,仍然都能够正常工作。所以如果想要更严谨,可以等待第二轮request发送完成之后,再次检查locust master的输出。200万请求全部成功,测试就通过了。