K6是“就像单元测试一样,以提高性能”

2020-11-28 12:26:05

k6是现代负载测试工具,它基于Load Impact在负载和性能测试行业的多年经验。它提供了一个干净,可访问的脚本API,本地和云执行以及灵活的配置。

一切都作为代码:测试逻辑和配置选项都在JS中,以实现版本控制的友好性

内置HAR转换器:将浏览器会话记录为.har文件,并将其直接转换为k6脚本

云执行和分布式测试(当前仅在Load Impact管理的基础架构上,计划在不久的将来在k6中进行本地分布式执行!)

您可以手动下载并安装正式的.msi安装软件包,或者,如果您使用Chocolatey软件包管理器,请按照以下说明设置k6存储库。

对于基于Debian的Linux发行版,您可以像这样从私有deb存储库中安装k6:

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 379CE192D401AB61 echo“ deb https://dl.bintray.com/loadimpact/deb stable main” | sudo tee -a /etc/apt/sources.list sudo apt-get更新udo apt-get install k6

wget https://bintray.com/loadimpact/rpm/rpm -O bintray-loadimpact-rpm.reposudo mv bintray-loadimpact-rpm.repo /etc/yum.repos.d/sudo dnf install k6#使用yum代替dnf对于较旧的发行版

如果没有适用于您的操作系统或体系结构的正式软件包,或者您不想安装自定义存储库,则可以轻松地从GitHub Releases页面获取预构建的二进制文件。下载并解压缩发行版后,可以选择将其包含的k6二进制文件复制到PATH中的某个位置,以便可以从系统上的任何位置运行k6。

k6是用Go编写的,因此它只是一个静态链接的可执行文件,非常易于构建和分发。要从源代码构建,您需要Git and Go(1.12或更高版本)。请遵循以下指示:

确保您的PATH中有$ GOPATH / bin(或在PATH中的某处复制k6二进制文件),以便可以从任何位置运行k6。

k6符合执行脚本的虚拟用户(VU)的概念-本质上来说,它们是美化的,并行while(true)循环。脚本是使用JavaScript作为ES6模块编写的,它使您可以将大型测试分解为更小,更可重用的部分,从而轻松地在整个组织中扩展测试。

脚本至少必须包含一个导出的默认函数-这定义了VU的入口点,类似于许多语言中的main()函数。让我们创建一个非常简单的脚本,该脚本向测试网站发出HTTP GET请求:

脚本的详细信息以及我们如何扩展和配置它的方法将在下面说明,但是现在,只需将上面的代码段另存为script.js文件在系统上的某个位置即可。假设您已正确安装k6,则在Linux和Mac上,可以通过从同一文件夹执行k6 run script.js来运行保存的脚本。对于Windows,该命令几乎相同-k6.exe运行script.js。

如果您决定使用k6码头工人镜像,则命令将略有不同。不是将脚本文件名传递给k6,而是使用破折号指示k6直接通过标准输入读取脚本内容。这样就可以避免使用如此简单的单文件脚本弄乱Docker卷,从而大大简化了docker命令:docker run -i loadimpact / k6 run-

在某些情况下,执行远程脚本也可能很有用。您可以使用k6中的HTTP S URL通过在URL中的脚本中导入它们或通过在CLI命令中简单地指定其URL来实现:k6运行github.com/loadimpact/k6/samples/http_2.js(k6“知道” a有关github和cdnjs URL的信息,因此该命令实际上是k6运行raw.githubusercontent.com/loadimpact/k6/master/samples/http_2.js的简写。

有关如何开始运行k6的更多信息,请查看“运行k6”文档页面。如果您想了解更多有关使用k6发出和测量HTTP请求的信息,请在这里和这里看看。有关诸如分布式云执行(k6 cloud命令)或云结果(k6 run -o cloud)之类的商业k6服务的信息,您可以访问k6.io或查看云文档。

在本节中,我们将简要探讨k6如何工作的一些基本概念和原理。如果您想更深入地了解k6脚本API,结果输出和功能,可以访问完整的k6文档网站,网址为k6.io/docs。

在前面的“运行k6”部分中,我们提到脚本必须包含默认功能。您可能会问:“为什么不从头到尾正常地运行我的脚本”,答案是:我们这样做了,但是默认函数内部和外部的代码可以做不同的事情。

每个虚拟用户(VU)都在一个完全独立的JavaScript运行时中(与所有其他正在运行的VU并行)执行脚本。默认功能内的代码称为VU代码,并且在测试运行期间不断运行。默认功能之外的代码称为初始化代码,并且在初始化该VU时,每个VU仅运行一次。

VU代码可以发出HTTP和Websocket请求,发出度量标准,并且通常执行您希望负载测试执行的所有操作,但有一些重要的例外情况-您无法从本地文件系统加载任何内容,也不能导入任何其他模块。所有这些都必须通过init代码完成。

有两个原因。首先是:性能。如果您在每次脚本迭代中都从磁盘读取文件,那么它的速度将变得不必要。即使您缓存文件和任何导入的模块的内容,这也意味着脚本的首次运行将比其他所有脚本都要慢得多。更糟糕的是,如果您有一个脚本只能根据运行时已知的内容来导入或加载内容,则每次加载新内容时都会引发缓慢的迭代。这也是为什么我们在执行所有必需的VU之前通过执行默认功能来开始实际负载测试之前对其进行初始化的原因。

但是还有另一个更有趣的原因。通过强制所有导入和文件读取进入init上下文,我们设计了分布式执行。我们知道将需要哪些文件,因此我们仅将这些文件分发到群集中的每个节点。我们知道将导入哪些模块,因此我们可以从一开始就将它们捆绑在一个档案中。并且,结合以上性能点,其他节点甚至不需要可写文件系统-所有内容都可以保存在内存中。

这意味着,如果您的脚本在以本地运行的k6执行时可以运行,那么它也可以在分布式执行环境(如k6云(在商业性的k6云基础架构中执行该脚本))中进行任何修改,也可以在将来按计划进行修改而工作k6本机集群执行模式。

为简单起见,与许多其他JavaScript运行时不同,k6中的许多操作都是同步的。这意味着,例如,正在运行的k6示例脚本中的let response = http.get(“ https://test-api.k6.io/”)调用将阻止VU执行,直到HTTP请求完成为止,保存响应变量中的响应信息,然后才继续执行脚本的其余部分-无需回调和Promise。

这种简化之所以有效,是因为k6不仅仅是一个JavaScript运行时。而是每个VU与所有其他正在运行的VU并行地在自己独立的半隔离Ja​​vaScript运行时中独立执行提供的脚本。这使我们可以充分利用现代的多核硬件,同时通过主要具有同步功能来降低脚本的复杂性。在有意义的地方,我们还具有VU内并行化功能,例如http.batch()函数(该功能允许单个VU像浏览器/真实用户一样发出多个同时的HTTP请求)或websocket支持。

另外,还有一个实际的sleep()函数!您还可以使用VU分隔在同一VU中的两次迭代(即默认功能的执行)之间重用数据:

默认情况下,如果未指定任何内容,则k6将仅使用1个VU运行脚本,并且仅进行1次迭代。对于调试很有用,但在进行负载测试时通常不是很有用。为了在负载测试中实际执行脚本,k6提供了很大的灵活性-您可以使用几种不同的配置机制来指定脚本选项,还可以使用几种不同的选项来控制VU的数量以及执行脚本的时间,除其他事项外。

假设您要在脚本中指定VU的数量。按照优先顺序,您可以使用以下任何一种配置机制来做到这一点:

命令行标志:k6运行--vus 10 script.js,或者如果要保存3次击键,则通过短-u标志语法(k6运行-u 10 script.js)。

环境变量:在使用k6运行脚本之前,先设置K6_VUS = 20。在使用docker k6映像以及在Kubernetes等容器化环境中运行时特别有用。

您的脚本可以导出k6读取并用于设置所需选项的options对象。例如,设置VU如下所示:

export let选项= {vus:30,};导出默认功能(){/ * ...执行任何操作... * /}

此功能非常有用,因为在这里您可以访问k6通过全局__ENV对象公开给脚本的键值环境变量,因此您可以使用JavaScript的全部功能来执行以下操作:

if(__ENV。script_scenario ==“ staging”){导出let选项= {/ *第一组选项* /}; } else {export let options = {/ *第二组选项* /}; }

或上述内容的任何变体,例如导入不同的配置文件等。此外,将大多数脚本配置都紧紧放在脚本代码旁,这使得k6脚本非常易于版本控制。

全局JSON配置。默认情况下,k6在当前用户的config home文件夹中查找它(取决于OS,对于Linux / BSD,k6将在$ {HOME} /。config / loadimpact / k6内查找config.json),尽管可以使用--config / -c CLI标志进行了修改。它使用与脚本文件中导出的选项相同的选项键,因此我们可以通过使config.json包含{“ vus”:1}来设置VU。尽管在那里很少设置VU的数量是没有道理的,但是全局配置文件对于存储诸如k6 login子命令所使用的不同输出的登录凭据之类的东西更加有用。

配置机制确实具有优先顺序。如图所示,列表顶部的选项可以覆盖列表下方指定的配置机制。如果我们使用上述所有示例来设置VU数量,则最终将获得10个VU,因为CLI标志具有最高优先级。另外请注意,并非所有可用选项都可以通过所有不同的机制进行配置-有些选项可能无法通过简单的字符串指定(因此没有CLI /环境变量),而其他一些很少使用的选项可能有意从CLI标志中排除了为了避免混乱-请参阅选项文档以获取更多信息。

如上所示,有几种方法可以配置同时启动的虚拟用户数k6。还有其他方法可以指定这些虚拟用户将运行多长时间。对于简单的测试,您可以:

通过--duration / -d CLI标志(或K6_DURATION环境变量和持续时间脚本/ JSON选项)设置测试持续时间。为了易于使用,使用人类可读的值(例如1h30m10s-k6运行--duration 30s script.js,k6 cloud -d 15m10s script.js,导出K6_DURATION = 1h等)来指定持续时间。如果设置为0,则k6不会除非用户手动将其停止,否则请停止执行该脚本。

使用--iterations / -i CLI标志(或K6_ITERATIONS环境变量和迭代脚本/ JSON选项)设置脚本迭代的总数。每当迭代总数(即所有VU的迭代次数)达到指定的次数时,k6将停止执行脚本。因此,如果让k6运行--iterations 10 --vus 10 script.js,则每个VU只会进行一次迭代。

对于更复杂的情况,您可以指定执行阶段。它们是持续时间,目标-VU对的组合。这些对指示k6在指定期间内线性上升,下降或保持在指定的VU数量。可以通过stage脚本/ JSON选项将执行阶段设置为{duration:...,target:...}对的数组,或者通过--stage / -s CLI标志和K6_STAGES环境变量通过duration来设置执行阶段。 :target,duration:target ...语法。

例如,以下选项将使k6在3分钟的时间内从5个VU线性上升到10个VU(k6以VU数量开始,或者默认为1个),然后在10个VU处保持平稳5分钟,然后逐渐上升在接下来的10分钟内从10个VU上升到35个VU,最后再下降到0个VU持续90秒。

export let选项= {vus:5,阶段:[{持续时间:“ 3m”,目标:10},{持续时间:“ 5m”,目标:10},{持续时间:“ 10m”,目标:35},{持续时间:“ 1m30s”,目标:0},]};

或者,您可以使用CLI标志--vus 5 --stage 3m:10,5m:10,10m:35,1m30s:0或设置环境变量K6_VUS = 5 K6_STAGES =“ 3m:10,5m:10,10m :35,1m30s:0“即可达到相同的效果。

提示:除了通过上面简要提到的__ENV全局对象访问提供的环境变量外,您还可以使用执行上下文变量__VU和__ITER来访问当前VU编号和该VU的当前迭代编号。如果您希望VU执行不同的脚本/场景或帮助每个VU生成不同的数据,这些变量将非常有用。 http.post(“ https://some.example.website/signup”,{用户名:`testuser $ {__ VU} @ testsite.com`,/ * ... * /})

对于更复杂的场景,您可以使用k6 REST API和k6状态,k6缩放比例,k6暂停,k6恢复CLI命令来手动控制正在运行的k6测试。对于通过k6 cloud命令在托管基础结构上执行的基于云的测试,您还可以在执行负载测试时指定不同负载区域的VU分配百分比,从而为您提供可扩展且按地理位置分布的测试执行。

除了初始化代码和所需的VU阶段(即默认功能)(为每个VU运行的代码)之外,k6还像许多其他测试框架和工具一样,支持测试范围的设置和拆卸阶段。需要将设置和拆卸功能(如默认功能)导出。但是与默认函数不同,setup和teardown仅在测试中调用一次-在测试开始时,在初始化阶段之后,在VU阶段之前(默认函数)调用setup(),并在以下位置调用teardown():在最后一次VU迭代(默认功能)完成执行之后,测试结束。通过k6 cloud在分布式云执行模式中也支持此功能。

导出功能设置(){return {v:1}; }导出默认功能(数据){控制台。日志(JSON。stringify(数据)); }导出函数teardown(data){if(data。v!= 1){抛出新的错误(“不正确的数据:” +JSON。stringify(数据)); }}

返回的任何数据setup()的副本将作为第一个参数传递给默认函数的每次迭代,并在测试结束时传递给teardown()。有关更多信息和示例,请参考此处的k6文档。

默认情况下,k6会测量并收集有关脚本执行操作的大量指标-不同脚本迭代的持续时间,发送和接收的数据量,发出的HTTP请求的数量,这些HTTP请求的持续时间,甚至是多长时间特定HTTPS请求的TLS握手完成了吗?要在输出中查看这些内置指标的摘要,您可以运行简单的k6测试,例如k6运行github.com/loadimpact/k6/samples/http_get.js。此处的文档中提供了有关k6收集的不同内置指标的更多信息(以及如何从脚本内部访问其中的一些指标)。

k6还允许创建用户定义的计数器,仪表,速率和趋势指标。它们可用于更精确地跟踪和测量k6默认情况下的自定义子集,或用户想要的其他任何内容,例如跟踪从远程系统返回的非定时信息。您可以在此处找到有关它们的更多信息,并在此处找到其API的说明。

k6中的每个度量标准都附带有一组键值标签。其中一些是由k6自动添加的-例如,特定的http_req_duration指标可能具有方法= GET,status = 200,url = https://loadimpact.com等。可以由用户添加其他对象-通过标签选项在全局范围内进行测试运行,或者作为特定HTTP请求,Websocket连接,userMetric.Add()调用中的参数单独添加。

这些标签不会在k6测试结束时的简单摘要中显示(除非您在阈值中引用它们),但是如果您使用以下提到的任何输出,则它们对于筛选和调查k6测试结果非常有用。 k6还支持简单的层次结构组,以简化代码和结果组织。您可以在此处找到有关组,系统和用户定义标签的更多信息。

检查和阈值是k6的一些功能,这些功能使使用负载测试(例如单元测试和功能测试)非常容易,并将它们集成到CI(连续集成)工作流程中。

检查与断言类似,但不同之处在于它们不会停止执行。相反,它们只是存储检查,通过或失败的结果,并让脚本继续执行。检查对于将与HTTP请求/响应有关的断言进行整理非常有用。例如,确保HTTP响应代码为2xx。

阈值是全局通过/失败标准,可用于验证任何结果指标是否在指定范围内。他们还可以基于使用的度量标准标签,引用给定度量标准中的值的子集。阈值在k6脚本的选项部分中指定。如果在测试运行期间超过了它们,则k6将在测试完成时以非零代码退出,并且还可以选择提前中止测试。这使阈值非常适合作为CI工作流程中的检查!

从“ k6 / http”导入http;从“ k6”导入{检查,分组,睡眠};从“ k6 / metrics”导入{Rate}; //跟踪失败率的自定义指标var failureRate = new Rate(“ check_failure_rate”); //选项export let options = {阶段:[//在第一分钟内从1线性增加到50 VU {目标:50,持续时间:“ 1m”},//在接下来的3分钟30秒内保持50 VU {目标:50,持续时间:“ 3m30s”},//在过去30秒内从50 VU线性降低到0 50 VU {目标:0,持续时间:“ 30s”} //总执行时间约为5分钟] ,阈值:{//我们希望所有HTTP请求持续时间的第95个百分位数小于500毫秒“ http_req_duration”:[“ p(95)<500”],//具有staticAsset标记的请求应更快完成“ http_req_duration { staticAsset:yes}“:[” p(99)<250“],//基于我们定义并用于跟踪应用程序故障的自定义指标的阈值” check_failure_rate“:[//全局故障率应小于1%” rate <0.01“,//如果爬升超过5%{阈值:” rate&

......