本教程包括:
- 什么是k6,它是如何工作的
- 编写和执行k6性能测试
- 分析k6性能测试结果
性能测试衡量系统在承受各种工作负载时的表现。被测试的关键品质是稳定性和响应性。性能测试显示了系统总体上的健壮性和可靠性,以及具体的潜在突破点。在本教程中,你将使用k6对托管在Heroku平台上的一个简单的API进行负载测试。然后你将学习如何解释从测试中获得的结果。本教程是用k6进行HTTP请求测试的配套教程。
先决条件
要学习本教程,你将需要。
我们的教程是不分平台的,但使用CircleCI作为一个例子。如果你没有CircleCI账户,请**在这里注册一个免费账户。**
k6是一个开源框架,旨在为开发人员提供性能测试的乐趣。k6之所以能够脱颖而出,是因为它能够封装可用性和性能,同时还捆绑了一些工具,如执行脚本的命令行界面和监控测试运行结果的仪表板。

k6是用goja编程语言编写的,它是ES2015(ES6)JavaScript在纯Golang语言上的一个实现。这意味着你可以使用JavaScript来编写k6脚本,尽管语言语法将只与JavaScript ES2015语法兼容。
注意:k6不在Node.js引擎上运行。它使用Go的JavaScript编译器。
现在你知道了在 k6 引擎下运行的东西,为什么不着手设置 k6 来运行你的性能测试?
克隆资源库
git clone https://github.com/CIRCLECI-GWP/api-performance-testing-with-k6.git
下一步是在你的机器上安装 k6。
安装 k6
与 JavaScript 模块不同,k6 必须使用软件包管理器来安装。在 macOS 中,你可以使用Homebrew。在Windows操作系统中,你可以使用Chocolatey。在k6文档中还有更多适用于其他操作系统的安装选项。在本教程中,我们将遵循macOS的安装指南。在Homebrew中执行这个命令。
brew install k6
这是我们开始编写k6测试时唯一需要运行的命令。
编写你的第一个性能测试
伟大的工作!你已经安装了k6并知道它是如何工作的。接下来是了解k6测量什么,以及你可以用什么指标来编写你的第一个负载测试。负载测试通过模拟多个用户同时访问系统来模拟系统的预期使用情况。在我们的案例中,我们将模拟多个用户在指定时间段内访问我们的API。
负载测试我们的应用程序
例子中的负载测试衡量了我们的API系统在受到各种用户群的影响时的响应性和稳定性。实例测试确定了系统的突破点,以及可接受的限度。
在本教程中,我们将针对一个k6测试文件运行脚本,首先在Heroku上已经托管的API应用程序中创建一个todo。为了使这被认为是一个负载测试,我们必须包括可扩展的虚拟用户、可扩展的请求或可变时间等因素。在我们的案例中,我们将专注于在测试运行时将用户扩展到应用程序。这个功能是预置在k6框架中的,这使得我们的实施变得超级容易。
为了模拟这一点,我们将使用k6的执行器概念。执行者在k6执行引擎中提供马力,负责脚本的执行。运行者可以决定这些。
- 要添加的用户数量
- 要进行的请求的数量
- 测试的持续时间
- 测试应用程序收到的流量
在我们的第一次测试中,我们将使用k6执行器,采用k6所说的方法,即ramping up 。在ramping up方法中,k6逐步增加虚拟用户(VUS)来运行我们的脚本,直到达到一个峰值。然后它在规定的时间内逐渐减少数量,直到执行时间结束。
编写这个负载测试时,要把执行者、虚拟用户和测试本身的想法结合起来。这里有一个例子。
import http from 'k6/http';
import { check, group } from 'k6';
export let options = {
stages: [
{ duration: '0.5m', target: 3 }, // simulate ramp-up of traffic from 1 to 3 virtual users over 0.5 minutes.
{ duration: '0.5m', target: 4}, // stay at 4 virtual users for 0.5 minutes
{ duration: '0.5m', target: 0 }, // ramp-down to 0 users
],
};
export default function () {
group('API uptime check', () => {
const response = http.get('https://todo-app-barkend.herokuapp.com/todos/');
check(response, {
"status code should be 200": res => res.status === 200,
});
});
let todoID;
group('Create a Todo', () => {
const response = http.post('https://todo-app-barkend.herokuapp.com/todos/',
{"task": "write k6 tests"}
);
todoID = response.json()._id;
check(response, {
"status code should be 200": res => res.status === 200,
});
check(response, {
"response should have created todo": res => res.json().completed === false,
});
})
});
在这个脚本中,options 对象在规定的时间内逐渐增加用户的数量,在规定的阶段。虽然默认情况下没有定义执行者,但k6在options 对象中识别了stages ,durations ,和targets ,并确定了执行者是ramping-vus。
4 在负载测试脚本中,我们的目标是在1m 30 seconds 。因此,该脚本将逐步增加应用用户,并在该时间段内进行尽可能多的create-todo 请求迭代。
换句话说,我们将尝试在指定的时间范围内进行尽可能多的请求,同时用提供的阶段来改变活跃的虚拟用户数量。这个测试的成功将取决于应用程序在该时间段内成功发出的请求的数量。下面是我们测试的虚拟用户与时间的关系图。

这个ramp-up graph 说明,随着时间的增加,虚拟用户被逐步添加到负载测试中,直到达到用户数的峰值。在最后阶段,当虚拟用户完成他们的请求并退出系统时,这个数字就达到了。在本教程中,用户的峰值数量是4 。ramping-vus 执行器类型不是唯一可用于运行负载测试的类型。其他执行器在k6中也是可用的。使用方法取决于你要执行的性能测试的需要和性质。
check() 性能测试脚本的例子表明,我们可以使用k6来定义我们的测试,我们的机器将运行的虚拟用户的数量,还可以在我们的group() 块下创建不同的断言,用于执行脚本。
注意:k6中的组让你结合一个大型的负载脚本来分析测试的结果。检查是组内的断言,但工作方式与其他类型的断言不同,因为它们在失败或通过时不会停止负载测试的执行。
使用k6运行性能测试
现在你已经准备好开始执行测试,这将帮助你了解你的系统和应用程序的性能能力。我们将测试我们的Heroku免费动态器和部署在其上的托管Todo API应用程序的极限。虽然这不是一个运行负载测试的理想服务器,但它可以让我们确定系统何时以及在何种情况下达到极限。对于本教程,突破点可能出现在Heroku或我们的API应用程序中。这个测试将用虚拟用户(VUS)向Heroku发出多个HTTP 请求。从一个虚拟用户开始,逐步增加到4个,虚拟用户将在1分30秒的时间内创建todo项目。
注意:我们假设Heroku能够处理4个与我们的API交互的用户并发会话。
要运行前面代码片断中的性能测试,只需在我们的终端上运行这个命令。
k6 run create-todo-http-request.js
当这个测试完成后,你将得到k6测试运行的第一个结果。

用这个结果来验证性能测试执行了1分30.3秒,并且所有测试的迭代都通过了。你还可以验证在测试执行期间,4个虚拟用户总共进行了390次迭代(完成了创建todo项目的过程)。
你可以从数据中获得的其他指标包括。
checks, 在测试中声明的完成的check()断言的数量(全部通过)- 总
http_reqs,所有发出的HTTP请求的数量(780请求) - 创建新todo项目的迭代总数和检查我们Heroku服务器正常运行状态的HTTP请求的组合
这个测试是成功的,我们能够获得关于应用程序的有意义的信息。然而,我们还不知道我们的系统是否有任何突破点,或者在一定数量的并发用户开始使用我们的API后,我们的应用程序是否会开始出现奇怪的行为。这就是我们将在下一节尝试完成的工作。
添加另一个HTTP请求将帮助我们找出是否会破坏Heroku服务器,甚至是我们的应用程序。我们还将减少虚拟用户的数量和测试的时间,以节省资源。这些调整可以在配置部分进行,只需注释掉执行的最后两个阶段即可。
注意: "破坏应用程序 "是指在不改变资源或应用程序代码的情况下,使系统不堪重负,以至于返回错误而不是成功执行。这意味着系统只在改变用户数或请求数的基础上返回错误。
stages: [
{ duration: '0.5m', target: 3 }, // simulate ramp-up of traffic from 1 to 3 users over 0.5 minutes.
// { duration: '0.5m', target: 4 }, -> Comment this portion to prevent execution
// { duration: '0.5m', target: 0 }, -> Comment this portion to prevent execution
通过这种配置,我们已经能够缩短执行的时间,减少用于执行测试的虚拟用户的数量。我们将执行之前使用的相同的性能测试,但增加一个HTTP请求来。
- 取出创建的
Todo - 验证其
todoID与创建的内容是否一致 - 验证它的
state的创建是completed: false
要执行这个性能测试,你需要create-and-fetch-todo-http-request.js 文件,这个文件已经在我们克隆的版本库的项目根目录中。转到终端,执行这个命令。
k6 run create-and-fetch-todo-http-request.js
一旦这个命令执行完毕,你将得到测试结果。

这些结果显示,我们的测试实际上突破了免费Heroku dynos对我们应用程序的限制。虽然有成功的请求,但并非所有的请求都能及时返回响应。这就导致了一些请求的失败。只使用3个恒定的虚拟用户并执行测试30 seconds ,有56 完整的迭代。在这56次中,checks 的95.97% 是成功的。
失败的检查与获取我们创建的Todo项目的todoID 的响应有关。这可能是我们的API或系统中出现瓶颈的第一个指标。168最后,我们可以验证我们的http_reqs ,这是创建Todo项目、获取Todo项目数据和验证Heroku服务器正常运行时间的请求总数。
56 requests for each iteration * 3 - each of every request item
从这些测试运行中可以看出,性能测试指标并不是所有系统和应用程序都是标准的。这些数字可能会因机器配置、应用程序的性质,甚至是被测应用程序的依赖系统而有所不同。我们的目标是确保瓶颈被识别和修复。你需要了解你的系统可以处理的负载类型,这样你就可以为未来做计划,包括使用高峰和高于平均水平的资源需求。
在上一步中,我们能够为托管在Heroku免费计划上的应用程序执行我们的性能测试,并分析命令行结果。通过K6,你也可以使用K6云计算仪表盘来分析性能测试的结果。仪表板是一个基于Web的界面,允许我们查看性能测试的结果。
K6云配置和输出
运行k6性能测试并在你的终端上获得输出是很好的,但与团队的其他成员分享结果和输出也是很酷的。除了许多很酷的分析功能外,分享数据是k6仪表板真正的闪光点。
要配置k6仪表盘的输出,请用你之前创建的账户信息登录k6云。接下来,找到你的访问令牌。

一旦你进入API令牌页面,复制访问令牌,这样你就可以运行测试并将其上传到云端。
接下来,将K6_CLOUD_TOKEN 作为一个环境变量。在你的终端运行这个命令,以设置k6云的令牌。
export K6_CLOUD_TOKEN=<k6-cloud-token>
注意: 用你刚从仪表盘上复制的k6令牌替换例子中的云令牌值。
现在你可以执行你的负载测试了。使用这个命令。
k6 run --out cloud create-and-fetch-todo-http-request.js
--out cloud 告诉k6将结果输出到云端。当执行开始时,k6自动创建带有测试结果的仪表盘。你可以使用仪表板来评估性能测试的结果。
与终端输出相比,仪表板使结果的解释更方便,更容易分享。你上传的运行的第一个指标是请求的总数,以及不同的虚拟用户是如何提出请求的。还有关于不同用户何时发出请求的信息。

这张图显示了虚拟用户的总数与所发出的请求的数量,以及每个请求的平均响应时间。当你对每个请求进行深入研究时,它就会变得更加有趣。k6仪表盘使用groups() 和checks() ,使之成为可能。
试试这个:选择检查标签,然后用树状视图过滤结果。

测试运行中的所有请求都按时间顺序列出,同时评估失败和成功的执行率,以及所有请求的平均时间和执行请求的总数量。
要评估单个请求和它们的时间,在同一个性能洞察页面中选择HTTP标签。你可以查看所有的请求,它们的执行时间,以及与其他请求的执行情况的比较。例如,一个请求属于第95个百分点,而另一个请求则是第99个百分点。仪表板显示了执行最长的请求所花费的最大时间,并有一个标准偏差数字供参考。该页面为你提供了几乎所有种类的数据点,你和你的团队可以用它来识别瓶颈。

你也可以使用k6仪表盘来选择一个特定的请求,并对照平均运行时间确定其性能。这对识别问题很有帮助,比如在多个用户访问资源时,大量锁定的资源造成的数据库瓶颈。
你和你的团队可以从使用k6仪表盘的力量中获益良多。
你的下一步是用CircleCI触发k6性能测试,并将其配置为在每次部署时运行。
设置Git并推送到CircleCI
注意:如果你已经克隆了项目仓库,你可以跳过本教程的这一部分。如果你想学习如何设置自己的项目,我把步骤加在这里。
为了设置CircleCI,通过运行以下命令在项目中初始化一个Git仓库。
git init
接下来,在根目录下创建一个.gitignore 文件。在该文件中添加node_modules ,以防止npm生成的模块被添加到你的远程仓库。然后,添加一个提交,将你的项目推送到GitHub。
登录CircleCI,进入项目仪表板。你可以从与你的GitHub用户名或你的组织相关的所有GitHub仓库列表中选择你想建立的仓库。本教程的项目被命名为api-performance-testing-with-k6 。在项目仪表板上,选择选项来设置你想要的项目。选择使用现有配置的选项。
注意: 启动构建后,预计你的管道会失败。你仍然需要将定制的.circleci/config.yml 配置文件添加到GitHub,以便项目能够正常构建。
设置CircleCI
在你的根目录下创建一个.circleci 目录,然后添加一个config.yml 文件。该配置文件保存了每个项目的CircleCI配置。使用该配置中的CircleCI k6orb 执行你的k6测试。
version: 2.1
orbs:
k6io: k6io/test@1.1.0
workflows:
load_test:
jobs:
- k6io/test:
script: create-todo-http-request.js
使用第三方orbs
CircleCI orbs是可重复使用的YAML配置包,将代码浓缩到一行。为了允许使用第三方的orbs,如python@1.2 ,你可能需要。
- 如果你是管理员,启用组织设置,或
- 向你的组织的CircleCI管理员申请许可。
现在,在进一步的调查中,你可以验证在k6中运行你的性能测试是成功的,而且它们已经被整合到CircleCI中。伟大的工作!

现在是时候添加指标来衡量每个端点的执行时间了。
评估API请求时间
从前面的性能测试可以看出,测试结构不如系统在任何特定时间对测试负载的反应重要。k6自带的指标功能叫Trend ,可以让你为你的控制台和云输出定制指标。你可以使用Trend ,通过自定义如何定义时间,找出向终端发出的每个请求的具体时间。下面是一个示例代码块。
import { Trend } from 'k6/metrics';
const uptimeTrendCheck = new Trend('/GET API uptime');
const todoCreationTrend = new Trend('/POST Create a todo');
export let options = {
stages: [
{ duration: '0.5m', target: 3 }, // simulate ramp-up of traffic from 0 to 3Vus
],
};
export default function () {
group('API uptime check', () => {
const response = http.get('https://todo-app-barkend.herokuapp.com/todos/');
uptimeTrendCheck.add(response.timings.duration);
check(response, {
"status code should be 200": res => res.status === 200,
});
});
要实现k6Trend ,从k6/metrics 中导入,然后定义你想要的每个趋势。对于本教程的这一部分,你只需要在进行正常运行时间检查或创建新的todo时检查API的响应时间。一旦创建了趋势,导航到具体的测试,并将你需要的数据嵌入到声明的趋势中。
使用命令执行你的todo创建性能测试文件。
k6 run create-todo-http-request.js
检查控制台的响应。你可以在克隆的版本库的根目录下找到这个测试文件。一旦在本地通过,提交并推送更改到GitHub。

一旦你的测试完成执行,你可以查看你在上一步中添加的趋势描述。每一个都有单个请求的时间,包括平均、最大和最小的执行时间以及它们的执行百分比。
总结
本教程向你介绍了什么是k6以及如何使用它来运行性能测试。你经历了创建一个简单的k6测试的过程,以创建一个todo列表项目,并针对Heroku服务器运行。我还向你展示了如何在命令行终端解释k6性能测试结果,以及如何使用云端仪表盘。最后,我们探讨了使用自定义指标和k6趋势。