3. k6 脚本基础:JavaScript 语法速览、模块导入、VU 与迭代、options 配置与 export default 函数
在编写 k6 压测脚本之前,必须掌握其核心的脚本模型。k6 脚本本质上是 JavaScript(ES6+),但运行在 Go 语言编写的 goja 引擎中,因此不支持浏览器 DOM API。本章将拆解 k6 脚本的五大基础构件,为后续编写多阶段压测场景打下坚实根基。
3.1 JavaScript 语法速览(k6 环境限定)
k6 支持 ES6 模块语法、箭头函数、模板字符串、解构赋值等现代特性,但存在以下关键限制:
| 特性 | 支持情况 | 说明 |
|---|---|---|
let / const |
✅ 完全支持 | 推荐使用 const 声明不可变引用 |
| 箭头函数 | ✅ 完全支持 | () => {} 常用于回调与默认函数 |
| 模板字符串 | ✅ 完全支持 | `${var}` 拼接 URL 或日志 |
| 解构赋值 | ✅ 完全支持 | const { key } = obj |
| Promise / async/await | ⚠️ 有限支持 | 仅用于 http.asyncRequest 等少数 API |
| DOM / window / document | ❌ 不支持 | k6 不是浏览器,无渲染引擎 |
CommonJS (require) |
❌ 不支持 | 必须使用 ES6 import |
💡 实战建议: 在 k6 脚本中始终使用
const 和 let,避免 var。所有 HTTP 调用必须通过 k6 内置模块,不能使用 fetch 或 axios。
3.2 k6 模块导入机制
k6 使用 ES6 import 语法加载模块。模块分为三类:
- 内置模块:
k6/http、k6/check、k6/metrics、k6/encoding等,直接通过路径导入。 - 本地模块:将公共函数或配置抽取为
.js文件,使用相对路径导入。 - npm 模块:k6 不支持直接
npm install,但可通过 k6 扩展(xk6)或 jslib.k6.io 加载部分社区库。
// 导入内置模块
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';
// 导入本地模块(假设文件位于同一目录)
import { generatePayload } from './helpers.js';
import { BASE_URL, HEADERS } from './config.js';
// 导入远程模块(k6 官方 CDN)
import { uuidv4 } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js';
⚠️ 常见错误: 不要使用
require(),k6 引擎会直接报错 require is not defined。所有导入必须写在文件顶部,不支持动态 import()。
3.3 VU 与迭代:k6 的执行模型
理解 VU(Virtual User) 和 迭代(Iteration) 是编写正确脚本的前提。k6 的执行模型可以用下图概括:
🔑 核心概念:
- VU:一个独立的虚拟用户,拥有自己的 JS 运行时和全局变量副本。VU 之间不共享内存。
- 迭代:每个 VU 重复执行
export default函数一次,称为一次迭代。 - 初始化:每个 VU 在首次运行时执行一次脚本顶部的代码(import、全局变量声明),之后只重复执行 default 函数。
3.4 options 配置:定义压测行为
export const options 是 k6 脚本的配置中心,用于定义 VU 数量、持续时间、阈值、阶段等。以下是一个包含多阶段场景的 options 示例:
export const options = {
// 基础配置
vus: 10, // 初始 VU 数
duration: '30s', // 测试持续时间
// 阶段配置(覆盖 vus 和 duration)
stages: [
{ duration: '30s', target: 20 }, // 阶段1:30秒内从 10 升至 20 VU
{ duration: '1m', target: 20 }, // 阶段2:保持 20 VU 运行 1 分钟
{ duration: '30s', target: 0 }, // 阶段3:30秒内降至 0 VU
],
// 阈值(SLO 门禁)
thresholds: {
http_req_duration: ['p(95)<500'], // 95% 请求响应时间 < 500ms
http_req_failed: ['rate<0.01'], // 失败率 < 1%
},
// 其他常用配置
iterations: 1000, // 总迭代次数(与 duration 互斥)
maxRedirects: 10, // 最大重定向次数
insecureSkipTLSVerify: true,// 跳过 TLS 证书验证
};
| 配置项 | 类型 | 说明 |
|---|---|---|
vus |
number | 并发虚拟用户数 |
duration |
string | 测试运行时长(如 '30s'、'5m') |
stages |
array | 多阶段压测配置,每个阶段包含 duration 和 target |
thresholds |
object | 性能门禁,定义通过/失败条件 |
iterations |
number | 总迭代次数(与 duration 二选一) |
scenarios |
object | 高级场景配置(可混合多种执行器) |
📌 阶段配置优先级: 当同时设置
vus/duration 和 stages 时,stages 会覆盖前两者。建议在复杂场景中只使用 stages 或 scenarios,避免混淆。
3.5 export default 函数:迭代的执行体
export default function() 是每个 VU 每次迭代都会执行的入口函数。它必须是一个函数,不能是箭头函数(因为需要绑定 this 上下文)。
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 5,
duration: '10s',
thresholds: {
http_req_duration: ['p(95)<200'],
},
};
export default function () {
// 1. 发送 HTTP 请求
const res = http.get('https://test.k6.io');
// 2. 执行检查(断言)
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 300ms': (r) => r.timings.duration < 300,
});
// 3. 模拟思考时间
sleep(1);
}
⚙️ 执行流程:
- k6 启动后,先执行脚本顶部的
import和全局变量声明(初始化阶段)。 - 根据
options创建指定数量的 VU。 - 每个 VU 独立、并发地重复调用
export default function(),直到达到duration或iterations限制。 - 每次调用之间,
sleep()控制迭代间隔,模拟真实用户行为。
3.6 完整脚本结构示例
将以上知识点整合为一个可运行的 k6 脚本模板:
// 1. 模块导入
import http from 'k6/http';
import { check, sleep, group } from 'k6';
import { Rate, Trend } from 'k6/metrics';
// 2. 自定义指标
const myFailRate = new Rate('my_failed_requests');
const myTrend = new Trend('my_response_time');
// 3. 配置
export const options = {
stages: [
{ duration: '10s', target: 10 },
{ duration: '20s', target: 10 },
{ duration: '10s', target: 0 },
],
thresholds: {
my_failed_requests: ['rate<0.05'],
http_req_duration: ['p(95)<1000'],
},
};
// 4. 默认函数(迭代主体)
export default function () {
group('用户登录流程', function () {
const payload = JSON.stringify({ username: 'test', password: '123' });
const params = { headers: { 'Content-Type': 'application/json' } };
const res = http.post('https://api.example.com/login', payload, params);
const passed = check(res, {
'登录成功': (r) => r.status === 200,
'返回 ': (r) => r.json('') !== undefined,
});
myFailRate.add(!passed);
myTrend.add(res.timings.duration);
});
sleep(1);
}
⚠️ 避坑指南:
- 不要在
export default外部编写需要重复执行的逻辑——它只会在初始化时运行一次。 - 不要在脚本中使用
console.log打印大量数据,会严重影响性能。使用 k6 的metrics或check记录结果。 - 每个 VU 的全局变量是隔离的,不要依赖 VU 间共享状态。如需共享,使用
k6/execution模块的vu.idInTest或外部数据源。