引言
在性能测试领域,JMeter 凭借其丰富的 GUI 和广泛的插件生态,长期占据着主导地位。然而,随着 DevOps 和持续交付理念的普及,脚本化、轻量级且易于集成到 CI/CD 管道的工具越来越受到青睐。k6 作为一款基于 Go 和 JavaScript 的现代负载测试工具,以其简洁的语法、出色的并发性能以及原生支持 InfluxDB、Prometheus 等监控生态,成为许多团队进行性能回归和容量评估的新选择。
JMeter 与 k6 的简要对比:
| 特性 | JMeter | k6 |
|---|---|---|
| 脚本语言 | Java/XML(GUI 或 DSL) | JavaScript(ES6) |
| 学习曲线 | 较陡,需熟悉 GUI 组件 | 平缓,适合前端/后端开发人员 |
| 执行模式 | 多线程(依赖 JVM) | 轻量级协程(Go) |
| 资源消耗 | 较高 | 极低 |
| CI/CD 集成 | 需借助命令行或 Maven 插件 | 原生支持,一条命令即可运行 |
| 监控集成 | 需插件或自定义 | 原生支持 InfluxDB、Prometheus、StatsD |
| 报告与可视化 | 聚合报告、HTML 报告 | 内置 Web Dashboard、Grafana 集成 |
本文旨在为读者提供一套完整的 k6 实战指南,从编写登录脚本 (获取并保存认证凭证)到编写业务查询脚本 (模拟并发场景),再到将指标输出至 InfluxDB 并通过 Grafana 构建定制仪表板,实现测试结果的实时可视化。文章中的所有接口地址、参数、Cookie 值等均为示例占位符,不涉及任何真实数据。
一、编写登录脚本(获取凭证并保存至 CSV)
1.1 业务流程概述
目标系统的登录流程基于统一认证协议,一般需要三步交互:
-
发起登录请求:提交用户凭证(如手机号、密码)及加密令牌,响应中返回临时票据。
-
换取授权码 :携带临时票据请求授权接口,获取用于后续验票的
ticket参数。 -
验票并获取最终凭证 :使用
ticket换取完整的认证 Cookie,该 Cookie 将用于所有后续业务请求。
登录成功后,我们将两个用户的完整 Cookie 按行写入 CSV 文件,供后续业务场景复用。
1.2 核心实现思路
在 k6 中,我们利用 http 模块发送请求,并使用 response.cookies 对象自动解析 Set-Cookie 头,避免手动正则提取。主要逻辑如下:
-
定义环境变量(如目标域名、协议)和固定加密参数(由开发提供,直接硬编码)。
-
实现三个核心函数,分别对应上述三步,每个函数接收必要的参数(如用户名、临时票据),返回提取到的关键值。
-
在
default函数中,从 CSV 数据文件中读取用户信息,依次为两个用户执行登录流程,若成功则将两个用户的 Cookie 拼接成一行记录,并通过console.log输出。
代码结构抽象示例:
javascript
// 步骤1:登录获取临时票据
function step1_login(username, password) { ... return { ticketA, ticketB }; }
// 步骤2:使用临时票据换取 ticket
function step2_getTicket(ticketA, ticketB) { ... return ticket; }
// 步骤3:验票获取最终 Cookie
function step3_validate(ticket, ticketB) { ... return cookieString; }
// 主循环
export default function () {
const user = getNextUser();
const temp = step1_login(user.tel);
const tic = step2_getTicket(temp.ticketA, temp.ticketB);
const cookie = step3_validate(tic, temp.ticketB);
console.log(`RECORD:${user.name},${user.tel},${cookie},...`);
}
实际实现中,我们利用了 k6 的
response.cookies自动管理 Cookie,并使用了extractAndAssembleCookies工具函数将响应中的所有 Cookie 拼接为请求头所需的格式。
1.3 生成 CSV 文件
运行登录脚本,并通过管道提取 RECORD: 行写入文件(PowerShell 示例):
powershell
k6 run login.js 2>&1 | Select-String 'RECORD:' | ForEach-Object { $_ -replace '.*RECORD:', '' } | Set-Content -Encoding UTF8 cookies.csv
生成的 CSV 表头为:姓名1,手机号1,cookie1,姓名2,手机号2,cookie2,单位编码,每一行包含两个用户的完整 Cookie。
二、编写业务场景脚本(模拟并发查询)
业务接口为 POST /api/your-business-endpoint,需要携带登录 Cookie。我们加载上一步生成的 CSV,构建并发测试。
2.1 加载 CSV 数据
使用 k6 内置的 open() 和 SharedArray 手动解析(避免第三方库):
javascript
import { SharedArray } from 'k6/data';
function parseCSV(content) {
const lines = content.trim().split('\n');
if (lines.length === 0) return [];
const headers = lines[0].split(',').map(h => h.trim());
const data = [];
for (let i = 1; i < lines.length; i++) {
const values = lines[i].split(',').map(v => v.trim());
if (values.length === headers.length) {
const row = {};
headers.forEach((h, idx) => { row[h] = values[idx]; });
data.push(row);
}
}
return data;
}
const cookieData = new SharedArray('cookies', () => {
const content = open('./cookies.csv');
const rows = parseCSV(content);
return rows.filter(row => row.cookie1 && row.cookie1.trim() !== '');
});
2.2 配置并发选项
我们设置 5 个并发虚拟用户(VU),持续 60 秒,并设定性能阈值:
javascript
export const options = {
vus: 5,
duration: '60s',
thresholds: {
http_req_duration: ['p(95)<3000'], // 95% 请求低于 3 秒
http_req_failed: ['rate<0.01'], // 错误率低于 1%
},
};
2.3 构建请求与断言
default 函数中,根据 VU 索引循环取 Cookie,构造 JSON 请求体,发送 POST 请求,并校验状态码和业务返回标志。
javascript
function getCurrentDate() {
const d = new Date();
return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
}
export default function () {
const idx = (__VU - 1) % cookieData.length;
const cookie = cookieData[idx].cookie1 || '';
const url = `https://${CONFIG.baseUrl}/api/your-business-endpoint`;
const body = JSON.stringify({
// 请求体结构(示例占位)
filters: [],
page: { pageNum: 1, pageSize: 100 },
// 其他参数
});
const res = http.post(url, body, {
headers: {
'Content-Type': 'application/json',
'Cookie': cookie,
'User-Agent': 'k6-test',
},
tls: { insecureSkipTLSVerify: true }, // 测试环境可关闭证书校验
});
check(res, {
'状态码200': (r) => r.status === 200,
'业务成功': (r) => r.body && r.body.includes('"flag":"success"'),
});
}
说明 :我们移除了
sleep(1),使 VU 连续发送请求,以模拟无思考时间的负载模式,从而获得更高的吞吐量。
三、执行测试并输出到 InfluxDB
k6 支持直接将指标写入 InfluxDB(v1.x),命令格式如下:
bash
k6 run --out influxdb=http://<influxdb-ip>:8086/k6 business_test.js
执行后,控制台会实时输出测试汇总。本次 60 秒测试,5 个 VU 共完成 10650 个请求,吞吐量约 177 req/s,平均响应时间约 27.8 ms,错误率为 0。
接下来我们将配置 Grafana 可视化这些指标。
四、Grafana 仪表板配置与结果查看
4.1 配置 InfluxDB 数据源
-
登录 Grafana,点击左侧齿轮图标 →
Data Sources→Add data source。 -
选择
InfluxDB。 -
URL 填写
http://<influxdb-ip>:8086,Database 填写k6。 -
保存并测试连接。
4.2 导入定制仪表板
我们基于官方模板调整了查询,使 Stat 面板显示的值与 CLI 输出完全一致。关键改动:
-
对于累计指标(如
http_reqs),使用last("value")或max("value"),避免因时间分组导致的数值偏差。 -
在趋势图中保留
GROUP BY time($__interval)以展示随时间的变化。
下面是一份可直接导入的仪表板 JSON 核心面板示例(完整 JSON 略长,但核心查询如下):
-
总请求数 :
SELECT last("value") FROM "http_reqs" WHERE $timeFilter -
吞吐量 :
SELECT last("value") / $__range_s FROM "http_reqs" WHERE $timeFilter -
响应时间统计 :
min、median、mean、percentile(95)、max,均不带 GROUP BY。 -
趋势图:保留分组聚合。
在 Grafana 中点击 + → Import,粘贴完整 JSON 内容,选择数据源即可导入。
4.3 调整时间范围查看结果
导入后,点击右上角时间选择器,选择 Last 15 minutes 或自定义测试起止时间,确保覆盖整个测试窗口。此时,所有面板数值应与 CLI 输出保持一致(例如总请求数 10650,吞吐量 177.5 req/s,错误率 0%)。
五、结果对比与一致性验证
我们分别执行了 k6 与 JMeter 在相同并发(5 VU/线程)和相同时间(60 秒)下的测试,结果对比如下:
| 指标 | JMeter | k6 |
|---|---|---|
| 总请求数 | 10609 | 10650 |
| 吞吐量 (RPS) | 176.9 | 177.3 |
| 平均响应时间 | 27 ms | 27.76 ms |
| 错误率 | 0% | 0% |
两者数据高度吻合,证明了 k6 的脚本逻辑与性能表现与 JMeter 等效。差异主要源于网络波动和系统缓存,属于正常范围。
六、总结与最佳实践
-
Cookie 管理 :利用
response.cookies可以简化 Cookie 提取,避免手动正则。 -
CSV 驱动 :使用
SharedArray确保所有 VU 共享数据,减少内存开销。 -
思考时间 :若需模拟无等待压力,务必移除
sleep,否则吞吐量将受限于1/sleep_time。 -
InfluxDB 聚合 :累计指标应使用
last或max,避免sum重复计数。 -
Grafana 时间范围:确保仪表板时间范围覆盖测试时段,否则数据不完整。
通过本文的实践,我们成功构建了一套从脚本开发、并发测试到实时监控的完整 k6 工作流。无论您是刚开始接触 k6,还是希望从 JMeter 迁移,本文提供的方案都能为您提供有价值的参考。欢迎留言交流讨论。