Open Source 28 January 2021

比较k6和JMeter的负载测试

Nicole van der Hoeven(k6开发者推广大使)著,Ng Wai Foong 译

📖你将会学到什么呢?

  • JMeter和k6的优缺点
  • 选择负载测试工具时需要考虑的事项
  • 在不同情况下,哪个工具更适合

引言

当我11月底加入k6团队时,我想到的第一个问题是“k6与JMeter相比如何?”,1. 我来自于一个性能测试咨询背景,在过去的几年中,JMeter一直是我的首选工具。

那么,我在k6网站上做什么,谈论我有多热爱JMeter?当然不是,首先,k6团队是相当开放和透明的,尤其是关于k6的潜在改进, 其次,我想分享一下我为什么选择使用k6作为负载测试工具的原因,也是最终说服我加入这个团队的原因。

什么是JMeter?

JMeterApache Foundation完全用Java构建的开源负载测试工具,它于1998年首次发布,慢慢开始流行起来,并与其他更有名但专有的负载测试工具竞争,相对于其他公司的高额收费,JMeter将其开源到网上供大家使用,而且是免费的。此外,JMeter支持通过用户界面(UI)和代码来创建自定义脚本,在撰写本文时,JMeter的最新版本是5.4。

什么是k6?

k6是LoadImpact在2017年发布的开源负载测试工具,LoadImpact专门提供软件即服务(SaaS)平台(现在称为k6 Cloud)和专业服务,k6是用Go编写的,而脚本是用JavaScript编写的。与JMeter相比,k6的主要卖点是基于代码的脚本,以及重视开发者体验,k6的最新版本是0.30。

哪个更好?

这篇文章不是关于哪个工具更好,因为没有“最好的工具”,一切都取决于你的团队所处的情况:测试什么,如何测试,为什么要测试,以及何时测试。

在这里我想说明的问题是:这两个工具擅长在哪里?


JMeter擅长在哪里?

当您想快速入门时

JMeter具有图形用户界面(GUI)

选择负载测试工具时,许多测试团队仅基于这一点。有时,对于初学者来说,基于GUI的工具(例如JMeter)是最佳的选择,对于许多习惯于使用无代码软件(例如Postman或SOAPUI)的测试员而言,带有GUI的工具更加容易使用。

JMeter的开始屏幕:一个全新的测试计划

我一直认为JMeter是用户友好的,但最近有位开发者朋友指出,当你看到“测试计划”屏幕时,屏幕上是不会显示如何创建HTTP采样器的任何指示,需要经过一些努力才能了解到正确的步骤:

  • 右键单击“测试计划”或者
  • 点击编辑(Edit) > 添加(Add)菜单

完成后,您将看到添加(Add)界面。这位朋友的说法是有道理的,用户界面在一定程度上是主观的,我认为对于非开发者来说,从UI入门绝对比代码简单得多。

k6工具中没有GUI包,但k6测试生成器(Test Builder)可以免费使用,这是一种使用GUI界面创建测试的方法,虽然它是基于k6 云(Cloud)的,但不需要任何订阅即可使用。此外,它不像JMeter那样功能齐全。

JMeter支持许多协议和功能

这个是JMeter所支持的协议的官方列表

  • 网络 - HTTP, HTTPS (Java, NodeJS, PHP, ASP.NET, …)
  • SOAP / REST 网络服务
  • FTP
  • JDBC数据库
  • LDAP
  • JMS面向消息的中间件(Message-oriented middleware),也称为MOM
  • Mail - SMTP(S), POP3(S) and IMAP(S)
  • 本地命令或Shell脚本
  • TCP
  • Java对象

相比之下,k6支持以下协议:

以上协议是本地支持的协议,这两个工具都允许开发第三方插件,增加对更多协议的支持,基于xk6k6社区扩展的数量在增长,但是JMeter插件仍然超过它们,在许多情况下,你的应用程序使用的协议可能会决定使用哪种工具。

JMeter还拥有基本负载测试所需的功能,且您不必自己编写代码,其父子(parent-child)元素结构允许你轻松地修改一个特定的元素,例如一个HTTP请求或所有HTTP请求,向所有请求添加思考时间(think time)是非常容易,您只需要向您的线程组添加定时器(Uniform Random Timer),该组中的所有请求都将继承它。相比下,基于代码的负载测试工具基本上很难做到这一点, 具有良好自动完成功能的IDE可以解决此问题。

当您更喜欢拥有大量文档的成熟社区时

JMeter自1998年成立以来,它在构建负载测试产品方面有大约22年的经验,您几乎可以在每个社区网络站点上找到有关JMeter的文章,而且无论您使用JMeter的用例有多具体,都有可能以前已经有人做过,当你搜索“如何用JMeter加载测试X”时,你会找到成千上万的文章和视频教程,教你如何操作。

JMeter的组件参考(Component Reference)是JMeter广泛而详细的文档的证明,每一个元素、功能、属性都有详细的讨论,这也是大多数人所希望的,除了Apache网站上的此“官方”文档之外,它的铁粉还提供了数千本与JMeter相关的书籍,教程和课程。

截至2020年12月,[JMeter GitHub存储库]有17,290项提交,有力的证据表明,在过去的二十年中,一群开发者向JMeter投入了他们的时间和经验。

相比之下,k6社区正在增长,但仍然很小,但比起JMeter,k6拥有一个官方的中央社区论坛,允许用户分享与k6相关的经验,JMeter社区里缺少此功能,k6存储库的确有更多的star,但是提交(commits)的数量却少得多,为3,795,这种差异的主要原因是因为k6非常新,这个开源工具才于2017年发布。

当您需要一种具有成本效益的方式来进行分布式负载测试时

JMeter最大的特点之一是为你提供了一个运行分布式负载测试的框架,对于一个免费的开源工具来说,这是相当特别的。

分布式测试意味着加大你的负载测试所产生的负载量,通常是通过增加虚拟用户的数量,并在其他负载生成器上运行多个脚本实例,JMeter通过指定一个controller node并让你设置worker node来实现这一点,每个worker node都需要有一份jmeter-server的副本,这是JMeter安装过程中包含的一个实用程序(在jmeter/bin文件夹中)。

JMeter提供原生分布式测试模式

来源: Apache JMeter

这些worker nodes运行测试计划的副本,并对应用程序服务器施加负载,您可以将本地服务器或云服务器作为您的worker nodes,类似这样的分布式执行需要一点时间来设置,但确实非常好用。

K6没有原生分布式负载测试模式,你可以自己将其容器化,并通过Kubernetes进行部署,但这个方案需要更多的设置,k6团队现在正在开发k6运算符(Operator),它将使设置变得更加简单,此功能仍然是实验性的,并具有诸如不支持实例之间的阈值同步的缺点,在k6上进行扩展的最简单方法是使用付费服务k6云(Cloud),这将显着减少设置时间和复杂性。

提醒一下,JMeter需要大量的资源,与k6相比,它需要多个负载生成器来生成负载,JMeter平均可以运行大约一千个虚拟用户,并且要进行扩展需要分布式设置。另一方面,在相同资源的情况下,单个k6实例可以运行成千上万的虚拟用户,我将在稍后解释更多关于这一点。

当您需要在负载测试工具中内置打包报告时

JMeter中的监听器(listener)允许您决定以何种格式查看结果。调试?使用View Results Tree,负载测试指标?使用Summary或Aggregate Report。

在JMeter中添加像View Results Tree这样的监听器,可以使您以不同的方式可视化结果

此外,JMeter可以根据您的负载测试生成HTML报告,目前这个功能虽然可用,但非常有限,它带有几个默认图形,可帮助您分析负载测试,它们不是交互式的,但足以作为生成测试报告模板的起点。

JMeter的内置HTML仪表板虽然可用,但非常有限

k6没有内置报告,它是模块化的,可以与许多其他数据分析工具集成,比如Grafana、Datadog、New Relic、Amazon CloudWatch和k6 Cloud。它们是更好的分析工具,可以提供比JMeter报告更多的见解,但这需要设置另一个工具来分析你的测试,如果你喜欢快速的解决方案并且对报告没有任何偏好,那么JMeter是一个更好的选择。

k6擅长在哪里?

当您想快速入门时

JMeter和k6都很容易上手,但是原因却截然不同。

安装

安装过程是非常快的,使用brew install k6命令,在我的macOS上安装k6只用了7秒,安装k6是不需要nodeJS或任何其他外部依赖项。

虽然JMeter很容易安装,但在安装前你必须安装Java,对于Windows用户来说,安装JMeter会多了一层复杂性,因为安装Java后,Windows用户需要将Java添加到环境变量。

确定正确的Java版本可能会让人困惑,因为Java安装包括JDK和JRE。此外,Oracle最近宣布,某些版本的Java需要使用许可证。

Oracle改变Java许可证给JMeter用户带来的困惑

来源: Oracle Java

插件

k6支持插件,但不是必须的,对于大多数用例,k6的内置功能应该是绰绰有余的。相比之下,在JMeter中,每样功能都是一个插件,甚至是插件管理器

大多数JMeter用户会说,在开始使用它之前,应下载一组标准的插件,新用户可能不知道在哪里可以找到这些插件,并可能会因此产生不愉快的体验。

当使用插件JMeter时,以下功能不可用:

  • 线程组(Thread groups):无自定义负载配置文件、阶段、集合点。
  • 控制器(Controllers):没有并行控制器来并行执行请求。
  • 采样器(Samplers):不支持HTTP/2。
  • 测试数据(Test Data):CSV文件中无随机行数(仅顺序排列)。
  • 定时器(Timers):没有吞吐量整形定时器来设置阶段性的吞吐量。

这些功能一开始就可以在k6使用。

脚本编写

k6脚本是用javascript编写的,因此编写和运行起来很简单,您只需要一个安装程序(在Microsoft Windows上)或在你的终端上的一个命令和任何文本编辑器, 对于JMeter,在开始之前,你需要在本地安装一些东西:Java,JMeter,以及您需要的标准插件,这可能会让新用户感到困惑。那么谁没有在学习使用JMeter时,特别是在windows上使用环境变量时没有遇到过困难?

学习新的用户界面(UI)也需要一定的时间,javascript几乎无处不在,当你要测试网站时,它是一门很好的语言。

当您想最大化性能和效率时

测试您的测试工具!

为什么这些都很重要?

在模拟许多虚拟用户时,性能慢等于成本高,负载生成器的数量是有成本的,不管是购置和维护的成本(本地)还是云服务的成本。

在使用负载测试工具来提高自己的应用程序性能之前,请考虑其性能限制。

k6建立在Go出色的性能上

k6是用Go编写的,而Go是为提高性能而构建的。“Go是一种编译型语言,与Java或Python不同,它不属于解释型语言,并没有任何多余的复杂性”,好处在于它没有外部依赖项,复杂度越低,性能测试工具的瓶颈来源越少。

负载测试工具运行虚拟用户的最简单也是最常见的方法是将一个虚拟用户分配给一个内核或操作系统线程,一个线程一个虚拟用户模式存在严重缺陷,当虚拟用户正在等待响应或正在执行sleep()命令时,线程会被阻止,无法运行其进程。

在k6中,每个虚拟用户都在goroutine而不是线程上运行,这有什么区别呢?goroutines可以由Go Scheduler控制,它就像一个交通警察,它通过“work stealing”和线程之间的工作交接,重用闲置的线程并智能地分配工作,这听起来是不是很熟悉?这与构建负载均衡器的原理相同,监督工作流程的外部监视器可提高总体绩效,Go在本质上以许多编程语言都没有的方式实现了负载平衡,这使其非常适合于负载测试工具。

能够利用Go的性能优化,也就意味着内存利用率大大降低,k6中的一个线程不会超过100kb,而JMeter使用的JVM线程则需要默认1MB,是k6的十倍。当然,Java允许用户调整应用程序的内存使用率,所以两者之间的差异并不会那么大,但是能看得出Go的起点要低得多。

... 在Java中创建[一个]新线程效率不高,每个线程消耗大约1MB的内存堆大小,并且如果您开始创建数千个线程,最终将由于内存不足而导致关闭。此外,如果您想在两个或多个线程之间进行通信,这是非常困难的。- Keval Patel

性能比较

需要更少的负载生成器

k6相对更好的性能意味着它需要更少的负载生成器来执行给定数量的负载,Rafaela Azevedo对k6和JMeter使用的内存进行了比较,结果如下

JMeter占用了760MB的内存。

在Rafaela Azevedo进行的独立测试中,JMeter占用了760MB的内存

来源: Rafaela Azevedo

其间,k6占用了256MB的内存。

在Rafaela Azevedo进行的独立测试中,k6占用了256MB内存,而同一测试中JMeter则为760MB

来源: Rafaela Azevedo

Rafaela的调查结果得到了我们自己的基准的进一步肯定。

最流行的开源负载测试工具的最大吞吐量和内存的比较

来源: Ragnar Lönn编写的负载测试工具比较

k6的内存占用率较低,所以它可以运行更多的虚拟用户,生成的负载也比一般的虚拟用户要大,无论您使用的是本地还是云中的负载生成器,使用k6时的成本都将减少,这种成本的节约,使得k6成为注重预算的团队的良好工具。

许多负载测试员的一个普遍误解是需要分布式执行(能够在多台计算机上启动负载测试的能力)以生成大负载,k6并非如此。- Rafaela Azevedo

k6在处理硬件资源的方式上与许多其他负载测试工具不同,单个k6进程将有效地使用负载生成器计算机上的所有CPU内核,单个k6实例足以产生3万到4万个虚拟用户(VUs)同时运行负载测试,此数量的VU每秒最多可生成30万个请求(RPS)。

除非您每秒需要超过10万到30万个请求(每分钟600万到1200万个请求),否则一个k6实例就足够了。 - k6文档

无内存不足错误

当您需要快速解决方案时,k6是很好的选择。另一方面,JMeter需要熟悉如何对Java进行性能调整,以及如何解决大部分常见的Java性能问题,我最常遇到的问题是:

ERROR - jmeter.threads.JMeterThread: Test failed! java.lang.OutOfMemoryError:
Java heap space.

只有看过这个错误的人才算是真正的JMeter负载测试员。:)

导致该错误的原因通常是分配给JMeter的堆内存不足,除了监控负载生成器的内存使用情况,你还需要监控JVM的内存使用情况,你可以通过修改JMeter二进制文件,并修改以下行来设置分配给JMeter的堆内存量:

# This is the base heap size -- you may increase or decrease it to fit your
# system's memory availability:
: "${HEAP:="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m"}"

甚至JMeter官方文档都建议您在运行首次负载测试之前就将Java堆大小增加到1GB以上。

增加Java堆大小,默认情况下,JMeter的堆容量为1GB,这对您的测试来说可能不够用,取决于你的测试计划和你想运行的线程数。- Apache JMeter

没有GUI意味着在负载测试期间没有资源开销

GUI通常会大大增加应用程序的开销,这也是为什么k6没有GUI的原因之一。

当你在运行负载测试时,最好的做法是通过命令行来运行,否则它会干扰您的结果。 从一开始,基于代码的负载测试工具就具有很高的性能。

在JMeter中,有很多关于GUI对性能影响的警告,这是文档中的一个:

不要使用GUI模式运行负载测试! - Apache JMeter

这是启动JMeter时出现的消息:

JMeter的启动屏幕警告用户不要使用GUI模式进行负载测试

没有GUI意味着少了一个问题,您在k6中创建的任何负载测试脚本都可以随时执行。

当您想进行面向目标的测试时

在开始一个新的负载测试项目时,对于一名测试员来说,最重要的问题是"为什么"?应该制定测试计划以直接解决团队希望进行负载测试的原因,应在非功能性要求中说明,然后应将其作为可接受性能的指导,阈值的一个常见示例是测试期间所有事务的平均响应时间少于3秒。

k6通过全局阈值(Global Thresholds)来实现了这一点,您可以创建自己的指标以在阈值中使用:

import http from 'k6/http';
import { Rate } from 'k6/metrics';
const myFailRate = new Rate('failed requests');
export let options = {
thresholds: {
'failed requests': ['rate<0.1'], // threshold on a custom metric
http_req_duration: ['p(95)<500'], // threshold on a standard metric
},
};
export default function () {
let res = http.get('https://test-api.k6.io/public/crocodiles/1/');
myFailRate.add(res.status !== 200);
}

来源: k6文档

JMeter本身不支持测试级的阈值,有几种方法可以解决此问题:

  • 持续时间断言(Duration Assertion): 您可以将持续时间断言添加到JMeter中的任何采样器,这样您就可以为该请求设置可接受的响应时间,以毫秒为单位,超过该时间,该请求(以及包含该请求的事务)将被标记为失败,这是在单个请求级别,甚至将持续时间断言元素放在“测试计划”级别也仅将相同的阈值应用于每个子请求,它并不构成整个考试的通过或失败标准。

  • 定时器(Timers): 对于与吞吐量相关的标准,您可以使用Constant Throughput Timer或Throughput Shaping Timer(插件),这种方法控制每秒花费多少请求,根据我的经验,你应该测试这些定时器在使用嵌套的事务控制器时的行为,可能需要一些时间才能正确使用。

  • 性能插件(Performance Plugin): 您可以将JMeter与Jenkins集成在一起,以使用Performance Plugin设置错误阈值,并在Jenkins中将测试运行(“构建”)标记为不稳定或失败。

  • 自定义代码(Custom Code): 您可以使用JSR223 Sampler编写一些Groovy或BeanShell代码来收集和解析响应指标。

尽管这些选项使在JMeter中设置阈值成为可能,但它们仍然是JMeter本身不支持的解决方法,它也不能充分解决创建不同类型阈值的问题(错误,响应时间,吞吐量,CPU,内存),我一般会将从JMeter导出原始数据,并使用其他工具自己进行分析。

当你是脚本团队的一员时

在一个人以上写脚本的情况下,k6是特别好用。

它为开发人员和测试人员提供了折中的中间立场

软件质量不应只是测试人员的责任,在构建软件时,质量应该参与到每一个部分, 落实这一概念的挑战之一是:“具有不同职能的团队成员所使用的工具和语言是不相同,开发者使用一种工具,测试员使用另一种工具。” 此外,在这种环境下,很难鼓励开发者进行测试,测试员进行编码,许多测试工具只会进一步鼓励这种分歧。

k6通过将测试概念和功能与开发者熟悉的环境和语言相结合来弥合差距,Javascript不仅用于编写负载测试脚本,而且k6可以使用任何IDE或文本编辑器执行,顺畅的开发者体验是k6的理念之一。

在脚本上进行协作更容易

一旦你说服了开发和测试人员都为负载测试脚本做出贡献,你如何跟踪脚本中的更改?您如何管理多个人同时编写的脚本?

由于测试是基于代码的,最简单的方法就是使用版本工具,比如Git,您可能已经在使用Git来跟踪您的应用程序代码的更改,这里没有新的框架或流程需要学习,完全不需要学习新的框架或程序。

k6的简洁性和可移植性被证明是协作期间的优势, 为了说明这一点,让我们创建一个简单的测试脚本,其中包含以下功能:

  • 我们的测试站点https://test.k6.io)提出GET请求。
  • 调用此请求的事务01_Home
  • 一个断言,它将在非HTTP 200响应或没有文本适合负载测试的简单网页集合的响应上失败。
  • 思考时间变量(think time)
  • 我们将以单个用户的身份运行它,进行一次迭代,脚本在k6中如下:
import http from 'k6/http';
import { sleep, check } from 'k6';
export default function () {
let res = http.get('https://test.k6.io', {tags: { name: '01_Home' }});
check(res, {
'is status 200': (r) => r.status === 200,
'text verification': (r) => r.body.includes("Collection of simple web-pages suitable for load testing")
});
sleep(Math.random() * 5);
}

您也可以在Git上跟踪JMeter脚本的更改。 这是JMeter中相同的脚本:

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.3">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">1</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<TransactionController guiclass="TransactionControllerGui" testclass="TransactionController" testname="01_Home" enabled="true">
<boolProp name="TransactionController.includeTimers">false</boolProp>
<boolProp name="TransactionController.parent">true</boolProp>
</TransactionController>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">test.k6.io</stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.protocol">https</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path"></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>
</HTTPSamplerProxy>
<hashTree>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings">
<stringProp name="-745788246">Collection of simple web-pages suitable for load testing</stringProp>
</collectionProp>
<stringProp name="Assertion.custom_message"></stringProp>
<stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
</hashTree>
<UniformRandomTimer guiclass="UniformRandomTimerGui" testclass="UniformRandomTimer" testname="Uniform Random Timer" enabled="true">
<stringProp name="ConstantTimer.delay">1000</stringProp>
<stringProp name="RandomTimer.range">4000</stringProp>
</UniformRandomTimer>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>

哪一个更容易理解? 如果您必须修改脚本以添加请求,您将选择以哪种格式进行更改?

XML脚本很容易导致合并冲突,并且很难查明自动化管道中的重大更改。

过去,我与其他人合作过JMeter脚本,我们总是在GUI中打开脚本,那里比较容易发现变化,但会引起新的问题。

当您想要易于维护的工具时

负载测试的版本管理

在将负载测试扩展到多个负载生成器时,跟踪必要组件的版本可确保不同版本之间的负载生成器流量相同,在科学实验中,减少变量的值对于负载测试而言是相同的。

至于k6,您只需要跟踪k6版本和脚本即可,不需要其他依赖项,如果您有使用插件,则也需要跟踪它们的更改,但是如前所述,k6中的插件不是必需的。此外,k6可作为Docker映像使用,以减少这些问题。

至于JMeter,需要考虑的层面更多(JMeter的版本、Java、插件的版本和脚本),每个新层都会引入更多不兼容性,在我以前的项目中,有人尝试了一个新插件而未与团队讨论,而任何试图在其JMeter副本中打开该脚本的人都会看到此错误:

这是当您尝试打开包含未在本地安装的插件的测试计划时,JMeter引发的错误

该错误表示存在一个脚本使用的插件,JMeter无法识别该脚本,找出正确的插件并不总是那么容易(在这种情况下,缺少的插件是Ultimate Thread Group)。

你绝对不希望这种情况发生在你的负载测试上,仅仅因为你忘了给你的一些负载生成器添加插件!

将测试代码与应用程序代码链接起来更容易

如果您的应用程序代码使用Javascript,那么k6是负载测试工具的不错选择,您可以通过导入对象来构建与应用程序代码集成的测试,这使您的测试可以直接与它们进行交互,当对象更改时,此方法将减少大量时间用于重构测试脚本。

如果您已经在使用VS Code,则k6甚至有一个VS Code插件

基于代码的测试使您可以控制

基于代码的脚本可减少歧义,您将不受不良的UI设计或第三方插件中的错误的影响,k6脚本基于Typescript,因此,大多数IDE在编写脚本时均支持自动补全,它等效于有用的工具提示,但更为通用,k6脚本允许你导入和使用JS库,你可以建立在别人的代码之上,虽然k6不是基于NodeJS的,但你可以捆绑一些NodeJS包在你的脚本中使用。

无外部依赖项

k6不需要NodeJS或其他程序运行,这减少了可能影响你的脚本的bug的可能性,由于JMeter运行在Java上,所以它依赖于Java,当Java更新版本时,JMeter也需要更新,JMeter的版本比Java版本晚了几个版本,但这可能会导致潜在的安全问题,而这些问题可能仍未被修复。

k6开发独立于任何第三方组件,因此可以轻松解决主要问题。

比较表:JMeter与K6

特征JMeterk6
基于JavaGo
脚本语言非常有限: Java (Groovy, Beanshell, etc)Javascript
协议通过插件支持大多数协议(本地支持HTTP/1.1、SOAP、FTP、JDBC、LDAP、MOM与JMS、SMTP、POP3、IMAP、外壳脚本、TCP、Java对象)支持较少的现代协议(HTTP/1.1, HTTP/2, WebSockets, gRPC)
外部依赖项Java
资源利用率差劲; 一个负载生成器可以模拟数千个虚拟用户非常好 ; 一个负载生成器可以模拟成千上万的虚拟用户
内存管理必须设置JVM堆内存本机使用负载生成器内存
线程模型1个线程1个虚拟用户:性能较慢,资源成本较高一个Goroutine1个虚拟用户:性能较快,资源成本更低
易于编写脚本基于图形用户界面,带代码块基于代码,VS Code插件
测试级阈值不支持,仅个人请求支持
脚本格式XMLjavascript
易于协作难以同时工作,易于测试员使用,需要GUI应用程序进行编辑易于开发者使用,易于版本; Javascript格式促进协作
维护详细的脚本,XML格式难以阅读更简洁的脚本; JavaScript易于阅读
社区自1998年以来,许多第三方教程,广泛的文档,没有中央社区自2017年以来,广泛的文档,更少的第三方教程,有官方社区
插件支持需要许多功能的插件,有很多可用的插件本机支持大多数功能,可提供插件支持,并且稀疏性
本机分布式负载生成否(仅限高级会员)
预先生成的报告默认和自定义HTML报告,通过监听器记录没有内置的预生成报告,通过第三方仪表板与分析工具集成
网站jmeter.apache.orgk6.io
源代码JMeterk6

结论

尝试选择负载测试工具时,最好的建议是与最有前途的候选人进行概念验证,在实际测试周期中使用时,某些功能或错误可能或多或少重要,不要把两个不同工具的负载测试结果看得太重,每个工具记录的指标都不同,并且使用同一工具将结果与以前的结果进行比较更有意义,切换工具时,每次都要在新工具中重新建立基线。

以下摘要可以帮助您在JMeter和k6之间进行选择。

两个工具都擅长:

  • 通过编写复杂的用户流脚本来在应用程序服务器上生成协议级负载、
  • 使用动态思考时间,测试数据生成和可重用或可自定义工作负载模型的逼真的脚本、
  • 功能文档和发布的一致性。

两个工具都不支持:

  • 生成浏览器级别的负载并与DOM元素进行交互,尤其是对于单页应用程序、
  • 详细结果分析(JMeter有预先生成HTML报告和侦听器,但还差得远),用户应该能够将结果与数据库和数据可视化软件集成在一起。

JMeter最适合:

  • 传统模式的测试团队、
  • 偏爱基于GUI的测试工具的开发者,这些工具具有大量的第三方教程和广泛的协议支持、
  • 以前是LoadRunner和NeoLoad等商业工具的用户。

k6最适合:

  • 协作和跨职能的工程团队,其中测试涉及多个角色、
  • 偏爱简单,轻量但功能齐全的负载测试工具的开发者、
  • 希望将测试集成到现有开发工作流和CI/CD管道中的团队。

负载测试脚本工具不是负载测试成功的最重要考虑因素,了解测试的原因,测试要求,理解和传达结果更为重要,使用正确的工具有助于顺利解决这些问题,没有“最佳”工具,只有适合项目和需求的工具。

参阅

< Back to all posts