经过了之前的铺垫:性能测试框架对比初探,目前留下来的几个测试框架就是JMeter、K6、locust和FunTester。本次测试目的是对比几种框架的在各个并发下面的发压能力和资源消耗。本次值测试了最简单的GET
接口,不涉及参数和POST
接口。
先说结论:
- 低并发(100 线程)情况下,FunTester资源消耗略微占优,但是高并发(200 线程)情况下K6牛逼,
golang
还是比较狂野,优势明显。 - 在尚未达被测服务性能拐点的时候,FunTester测试框架在资源消耗还有一些优势,但是达到拐点之后,由于线程的频繁上下文切换,K6的优势就非常明显了,总体来看大概两倍的差距。
- 本地测试也验证了上面两点,不过被测服务的 QPS 达到 6 万 +,而局域网被测服务最高 1.5 万徘徊。
准备工作
本机硬件2.6 GHz 六核Intel Core i7
,CPU 统计数据来自活动监视器
,100%
代表消耗了一个 CPU 线程,理论上全部 CPU 资源当做1200%
,内存数据也来自活动监视器
。
首先我利用FunTester moco server 框架架构图测试框架在局域网环境起了一个测试服务,只有一个兜底接口。Groovy
脚本如下:
import com.mocofun.moco.MocoServer
class TestDemo extends MocoServer{
static void main(String[] args) {
def log = getServerNoLog(12345)
log.response("hello funtester!!!")
def run = run(log)
waitForKey("fan")
run.stop()
}
}
同一局域网服务的性能没有问题的,跟本地启动服务区别在于,本地请求太快了,各种框架压测差不不够明显。而且测试本地服务,QPS 太高平均响应时间太低了,导致误差会比较大。
脚本准备
locust
本地Python
版本3.8,locust
默认下载版本:locust 1.5.3。
locust
框架只需要一个写好的Python
脚本,下面分享一下测试脚本。
第一版非常基础,但是实测太拉胯了,如下:
from locust import HttpUser, TaskSet, task
class UserBehavior(TaskSet):
@task(1)
def profile(self):
self.client.get("/m")
class WebsiteUser(HttpUser):
tasks = [UserBehavior]
min_wait = 5000
max_wait = 9000
经过FunTester 地球分社群友指正,这里使用了FastHttpUser
代替原来的HttpUser
,还有另外一个类似的框架FastAPI
,经过测试,性能提升一倍。最终版本内容如下:
from locust.contrib.fasthttp import FastHttpUser
from locust import HttpUser, TaskSet, task
class UserBehavior(TaskSet):
@task(1)
def profile(self):
self.client.get("/m")
class WebsiteUser(FastHttpUser):
tasks = [UserBehavior]
min_wait = 5000
max_wait = 9000
JMeter
本地Java SDK
版本1.8.0_281,运行方式采取了command
方式。GUI
是在太坑了。
由于 JMeter 不用脚本,是在没啥好分享了,一切默认,配置协议、地址、端口、接口路径即可。
配置文件内容:
<stringProp name="HTTPSampler.domain">192.168.80.169</stringProp>
<stringProp name="HTTPSampler.port">12345</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/m</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
K6
虽然k6是用golang
写的,但是测试脚本语言是JavaScript
,内容如下:
import http from 'k6/http';
import { sleep } from 'k6';
export default function() {
http.get('http://192.168.80.169:12345/m');
}
FunTester
本机Java SDK
版本同上,Groovy SDK
版本:Groovy Version: 3.0.8 JVM。Java
堆内存设置1G,其他参数默认。
本次默认使用看Groovy
测试脚本的方式,运行方式也是Groovy
脚本运行方式,是的,还有可以通过Java
方式运行,优点就是控制设置JVM
参数,实测影响不大。这个方式后期会在阿里开源龙井(Alibaba Dragonwell)时候会对比测试一下结果。
下面是脚本内容:
import com.funtester.config.Constant
import com.funtester.frame.execute.Concurrent
import com.funtester.frame.thread.RequestThreadTimes
import com.funtester.httpclient.ClientManage
import com.funtester.httpclient.FunLibrary
import com.funtester.utils.ArgsUtil
import org.apache.http.client.methods.HttpGet
class Share extends FunLibrary{
public static void main(String[] args) {
ClientManage.init(10, 5, 0, EMPTY, 0);
def util = new ArgsUtil(args)
int thread = util.getIntOrdefault(0,200);
int times = util.getIntOrdefault(1,10000);
String url = "http://192.168.80.169:12345/m";
HttpGet get = getHttpGet(url);
Constant.RUNUP_TIME = 0;
RequestThreadTimes task = new RequestThreadTimes(get, times);
new Concurrent(task, thread, "本地固定QPS测试").start();
testOver();
}
}
万事俱备,准备测试!!!
实战开始
我查资料的时候,很多直接从100线程
并发开始,以倍增甚至质数增长到上万的,但在实际使用中单机根本用不到,我本机测试性能拐点大概150左右,最终瓶颈点也在200以内。所以呢,我设置了四项10
、50
、100
和200
。
首先 10 线程肯定都是轻轻松松的,硬件资源都是足够的,可以做一个参照。50 线程算是一个中等压力,主要对比 10 线程,100 线程差不多性能已经比较高了,但是应该还没到拐点,200 线程应该是超过拐点,到达瓶颈点。
当然这里有照顾locust
的因素,经过我前期初测,实在没必要搞多节点的必要。
10 线程
测试结果:
框架 | CPU | 内存 | QPS | RT |
---|---|---|---|---|
JMeter | 37.58 | 472.7 | 1040 | 9 |
K6 | 53.54 | 78.2 | 2302 | 4.26 |
locust | 83.65 | 45.9 | 1049 | 8 |
FunTester | 28.82 | 385.3 | 2282 | 4 |
JMeter
和FunTester
内存都比较高,这个特点一直存在。实测结果中,k6
和FunTester
所测QPS
比较高,也比较接近,JMeter
和locust
基本砍半,补充测试JMeter GUI
测试结果更惨,还得砍。CPU
数据除了locust
以外其他都差不多,因为这个数据是我肉眼 记录感觉平均,实际测试过程中波动也比较大,误差是难免的。
盲猜locust
应该是花费了一些精力同步计算测试结果了。而JMeter
我是先测试后查看结果,应该排除了这个问题。
测到这里,locust
基本要被淘汰了,实在有点低,消耗 CPU 还多,不过还是下一轮还是测了locust
。
50 线程
测试结果:
框架 | CPU | 内存 | QPS | RT |
---|---|---|---|---|
JMeter | 120.71 | 776.7 | 3594. | 13 |
K6 | 161.02 | 107.9 | 9805 | 5.02 |
locust | 99.45 | 55.8 | 1424.52 | 27 |
FunTester | 88.64 | 392.6 | 9773 | 5 |
结论跟上一轮差不多,具体的各位可以看看数据。
整个测试过程中JMeter
数据中 QPS 波动过于大了,最低的不到2000
,上面是 QPS 最高的一次。而且我发现JMeter
对于端口或者连接利用率不是很好,QPS 一高起来,一会就报连接异常,网上一查说是端口不够用了,改完就好,继续增大线程,继续垮掉。可能是我姿势不太对,反正FunTester足够用。
经过查证,JMeter
端口数大概使用了线程数三倍再多一点的端口数。FunTester
用了两倍多一点,k6
一直比较稳定的低,一直在 50 以内。这一点我以后得研究研究继续优化。
接下来的测试我抛弃locust
,也抛弃JMeter
了,错误率太高了,测试过程中,JMeter 测试用例可读性差的问题,显露无疑。
100 线程
只剩下两个强者,测试结果:
框架 | CPU | 内存 | QPS | RT |
---|---|---|---|---|
K6 | 199.69 | 168.4 | 12631 | 7.84 |
FunTester | 225.74 | 424.7 | 13604 | 7 |
数据相差并不大,K6 消耗的 CPU 也逐渐降下来了,与FunTester很接近了,说明此时差不多应该是到了性能拐点附近。
200 线程
测试结果:
框架 | CPU | 内存 | QPS | RT |
---|---|---|---|---|
K6 | 239.97 | 240.4 | 15354 | 12.94 |
FunTester | 431.52 | 427.9 | 14940 | 13 |
这里可以看出K6
的优势还是非常明显的。初步判断应该到了瓶颈点,线程数增加了一倍,QPS 只增加了 10% 量级,而且响应时间明显升高。
后来我通过修改JVM
启动参数,增加堆内存,实际效果上没有明显提升。不得不说,golang
协程非常厉害,Java
协程这块有一个阿里龙井
的解决方案,开源免费,我暂时没有测试,有兴趣的可以了解一下,真的相当牛逼。
今天的对比测试结束了,有兴趣的同学可以到FunTester 地球分社中多多交流。
Goold Luck ! FunTester !