k6 负载测试工具:脚本化性能测试
k6 负载测试工具:脚本化性能测试完全指南
目录
什么是 k6 以及为什么选择它
k6 是一款现代的开源负载测试工具,专为开发者和 DevOps 团队设计。它使用 Go 语言编写引擎,但允许你通过 JavaScript(ES6) 编写测试脚本。这使得测试即代码(TaaC)的理念得以贯彻,性能测试可以像应用程序代码一样被版本控制、代码审查以及集成到 CI/CD 流水线中。
与传统工具的区别
- 脚本化优先:告别繁琐的 XML 配置,用你熟悉的 JavaScript 表达复杂的测试逻辑。
- 高性能低资源消耗:基于 Go 的高效 goroutine 调度,单个实例即可生成海量并发。
- 开发友好:内置 CLI、丰富的执行器(Executors)以及一键输出到多种后端(InfluxDB、Cloud、Prometheus 等)。
- 原生云原生:易于容器化,支持在 Kubernetes 中作为 Job 运行,也提供官方云服务 k6 Cloud。
如果你需要验证 API 在高流量下的表现、模拟用户峰值访问,或持续监控服务端性能,k6 是目前最流畅的解决方案之一。
环境准备与安装
本地安装
根据不同操作系统选择:
macOS
brew install k6
Windows
使用 Chocolatey:
choco install k6
或从 GitHub Releases 下载 .msi 安装包。
Linux (Debian/Ubuntu)
sudo apt-get update
sudo apt-get install k6
其他发行版请参考官方文档。
Docker 方式运行
docker pull grafana/k6
docker run --rm -i grafana/k6 run - <script.js
安装完成后,验证:
k6 version
应显示类似 k6 v0.49.0 的版本信息。
项目结构建议
为保持可维护性,推荐以下目录布局:
my-load-tests/
├── scripts/ # 测试脚本
│ ├── smoke.js # 冒烟测试
│ ├── load.js # 负载测试
│ └── stress.js # 压力测试
├── helpers/ # 可复用模块
│ └── http.js # 封装请求函数
├── data/ # 测试数据 (JSON/CSV)
└── results/ # 输出结果
编写你的第一个测试脚本
k6 脚本必须至少包含一个默认导出的函数,该函数将作为每个虚拟用户的入口点。
创建一个文件 api-test.js:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 10, // 同时运行的虚拟用户数
duration: '30s', // 测试持续时间
};
export default function () {
const res = http.get('https://test-api.example.com/users');
// 断言状态码与响应时间
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1); // 用户等待时间,模拟真实行为
}
在终端执行:
k6 run api-test.js
你将看到实时终端输出,包括虚拟用户数、请求速率、失败率以及标准检查点结果。测试结束后会打印包含百分位响应时间的摘要表格。
k6 的核心概念
虚拟用户与迭代
虚拟用户 (VUs) 是独立运行的并发执行单元。每个 VU 会不断循环调用 default 函数,一次执行称为一次迭代。
你可以通过 options 控制并发模式:
export const options = {
vus: 50, // 固定并发用户数
iterations: 1000, // 指定所有 VU 总共完成的迭代次数
};
若同时设置 duration 和 iterations,测试会在哪个条件先达到时停止。
生命周期
k6 脚本有四个生命周期阶段:
- init 阶段:加载导入的模块、读取文件、定义配置。此阶段每个 VU 共享一个 JavaScript 上下文,适合初始化全局数据。
- setup 阶段:在执行测试逻辑前运行一次,常用于获取认证令牌或数据预热。
- VU 阶段 (default 函数):每个 VU 独立执行的测试代码,是主要的性能测试负载。
- teardown 阶段:所有 VU 结束后运行一次,用于清理资源。
示例:
export function setup() {
// 获取 token
const res = http.post('https://auth.example.com/token', { login: 'user' });
return { token: res.json('token') };
}
export default function (data) {
// 使用从 setup 传递的数据
console.log(`My token: ${data.token}`);
// ... 请求逻辑
}
export function teardown(data) {
// 注销或清理
}
阈值与检查点
- Check(检查点):用于断言单个请求的正确性,但不停止测试。结果会统计到最终报告中。
- Thresholds(阈值):定义通过/失败标准,当指标超过设定值时测试会被标记为失败。
export const options = {
thresholds: {
http_req_failed: ['rate<0.01'], // 失败率必须低于1%
http_req_duration: ['p(95)<200'], // 95%请求响应时间低于200ms
},
};
阈值的表达式形式为 聚合函数(参数) <条件> <值>,例如 p(95)<500 表示第95百分位数要小于500毫秒。
高级脚本设计模式
数据驱动测试
对于需要参数化输入的场景,可使用 SharedArray 或 open 函数读取 CSV/JSON 文件。
读取 JSON 数据:
import { SharedArray } from 'k6/data';
const users = new SharedArray('users', function () {
return JSON.parse(open('./data/users.json'));
});
export default function () {
const randomUser = users[Math.floor(Math.random() * users.length)];
const payload = JSON.stringify(randomUser);
http.post('https://httpbin.org/post', payload);
}
SharedArray 确保数据只加载一次并共享给所有 VU,内存友好。
动态生成测试数据
利用 k6/crypto 和内置的随机函数:
import { randomIntBetween, randomString } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
export default function () {
const randomEmail = `user_${randomString(8)}@test.com`;
// ...
}
场景与执行器
k6 通过场景 (scenarios) 实现复杂的测试调度。每个场景可以指定独立的执行器和并发模型。
export const options = {
scenarios: {
// 场景1:逐步增加负载
ramping_load: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 100 }, // 爬升至100 VU
{ duration: '5m', target: 100 }, // 保持100 VU
{ duration: '2m', target: 0 }, // 降至0
],
gracefulRampDown: '30s',
},
// 场景2:恒定高速请求,不关心VU数量
constant_request_rate: {
executor: 'constant-arrival-rate',
rate: 500, // 每秒迭代次数
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 50,
maxVUs: 200,
},
},
};
常用执行器:
ramping-vus:适合模拟流量高峰。constant-arrival-rate:精确控制每秒请求数,推荐用于 API 压测。per-vu-iterations:每个 VU 执行固定次数迭代。
自定义指标与标签
你可以创建自定义指标收集业务维度的数据:
import { Trend, Counter, Gauge, Rate } from 'k6/metrics';
const waitTime = new Trend('wait_time', true); // true 表示包含在摘要中
const itemsProcessed = new Counter('items_processed');
const activeConnections = new Gauge('active_connections');
export default function () {
const start = Date.now();
// ... some operation
waitTime.add(Date.now() - start);
itemsProcessed.add(1);
activeConnections.add(1);
}
标签 (Tags) 允许按维度拆分数据:
http.get('https://api.example.com', {
tags: { endpoint: 'list-users', version: 'v1' },
});
之后在结果输出中可按 endpoint 分组查看统计。
结果分析与可视化
内置输出方式
k6 在运行结束后默认输出控制台摘要,也支持通过 --out 将实时指标流式发送到外部后端:
- InfluxDB + Grafana:经典的监控栈。
k6 run --out influxdb=http://localhost:8086/k6 script.js - Prometheus Remote Write:适合云端原生环境。
- CSV:
k6 run --out csv=results.csv script.js便于离线分析。 - k6 Cloud:官方 SaaS 服务,提供美观的图形化仪表盘和协作功能。
k6 login cloud --token <your-token> k6 run --out cloud script.js
在脚本中生成 HTML 报告
使用社区扩展 K6 HTML Report:
npm install -g k6-reporter
k6 run --out json=results.json script.js
k6-reporter results.json
将生成一个独立的 HTML 文件,包含图表和表格。
关键指标解读
运行结束后重点关注:
- http_req_duration:平均、中位数、p95、p99 响应时间。
- http_req_failed:失败率,通常与阈值结合判断健康状态。
- iterations:总迭代次数,反映测试吞吐。
- vus 和 vus_max:实际并发及最大并发。
分布式测试与云集成
对于需要超大规模负载的场景(如百万并发),单台机器可能成为瓶颈。k6 提供两种扩展路径:
使用 k6-operator (Kubernetes)
利用 Grafana k6-operator 在 Kubernetes 集群中运行分布式测试。它通过自定义资源 K6 将测试脚本分发到多个 Pod 并行执行,并自动聚合结果。
示例定义:
apiVersion: k6.io/v1alpha1
kind: K6
metadata:
name: my-distributed-test
spec:
parallelism: 10 # 10个并行执行器
script:
configMap:
name: my-test-script
file: test.js
每个执行器独立运行一段负载,最终将指标汇总。
k6 Cloud 上的分布式测试
在脚本中设置 options.cloud 或直接通过 CLI 参数启用云执行:
k6 cloud script.js
云服务自动调度多个负载生成区域,最大支持数百万 VU。
决策:若团队已有 K8s 基础设施,推荐使用 operator 保持控制权;若追求极简与快速扩展,k6 Cloud 是理想选择。
常见问题与调试技巧
1. “http_req_duration” 指标比实际感觉慢
- 检查是否包含了 DNS 解析和 TCP 连接时间。可使用
http_req_connecting、http_req_blocked等细分指标定位。 - 确认客户端网络是否有限制,或在同一机器上运行太多 VU 导致 CPU 饱和。
2. 脚本中使用了第三方 npm 包
k6 不是 Node.js,不支持完整 Node API。需要使用纯 JavaScript 库或官方 JSLib。将通过 webpack/rollup 打包的脚本导入 k6 是常用方案。
打包示例 (webpack):
npm init -y
npm install webpack webpack-cli
将脚本打包为单文件:
// webpack.config.js
module.exports = {
entry: './src/test.js',
output: { filename: 'bundle.js', path: __dirname },
mode: 'production',
target: 'web', // 注意,不是 node
};
然后 k6 run bundle.js。
3. 如何调试脚本
- 使用
--http-debug打印所有请求响应的详细信息:k6 run --http-debug="full" script.js - 在脚本中添加
console.log查看变量状态。 - 利用
--no-usage-report禁止发送匿名统计,清理输出。
4. 测试未达到设定的 VU 数量
可能受限于 --compatibility-mode 或系统文件描述符限制。检查 ulimit:
ulimit -n 100000
确保机器资源充足。
下一步学习资源
学习性能测试不仅仅是掌握工具,以下是官方和社区推荐:
- 官方文档:k6.io/docs – 最权威的命令参考和指南。
- k6 示例库:GitHub k6-community/examples 包含多种场景。
- 《k6 学院》视频:k6.io/academy 免费交互式课程。
- Grafana 负载测试博客:定期发布最佳实践。
- 参与社区:GitHub Discussions 与 Slack 频道 #k6,遇到问题积极提问。
开始你的脚本化性能测试之旅吧!试着对公司的 API 或一个公开接口编写负载测试脚本,并用阈值定义你的服务等级目标(SLO)。每一次按 Enter 运行,都是在为系统的可靠性增加一道防线。