性能框架哪家强—JMeter、K6、locust、FunTester横向对比

2021-07-23 13:43:49 浏览数 (1)

经过了之前的铺垫:性能测试框架对比初探,目前留下来的几个测试框架就是「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脚本如下:

代码语言:javascript复制
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脚本,下面分享一下测试脚本。

第一版非常基础,但是实测太拉胯了,如下:

代码语言:javascript复制
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,经过测试,性能提升一倍。最终版本内容如下:

代码语言:javascript复制
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不用脚本,是在没啥好分享了,一切默认,配置协议、地址、端口、接口路径即可。

配置文件内容:

代码语言:javascript复制
 <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,内容如下:

代码语言: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)**时候会对比测试一下结果。

下面是脚本内容:

代码语言:javascript复制

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」以内。所以呢,我设置了四项1050100200

首先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

JMeterFunTester内存都比较高,这个特点一直存在。实测结果中,k6FunTester所测QPS比较高,也比较接近,JMeterlocust基本砍半,补充测试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 !


0 人点赞