3. k6 基础架构:核心概念与运行原理
k6 采用 单进程、多协程(Go 语言 goroutine) 的架构,将每个虚拟用户(VU)抽象为一个轻量级 goroutine,而非操作系统线程。这种设计使得 k6 能够在单机节点上轻松模拟数万并发用户,同时保持极低的内存开销。理解 k6 的四个核心概念——VU、场景、指标、检查点——是掌握 k6 脚本编排与压测执行的基础。
核心认知: k6 的架构哲学是“脚本即配置”。VU、场景、指标、检查点均在 JavaScript 脚本中声明,无需外部配置文件,这与 JMeter 的 GUI 配置方式形成鲜明对比。
3.1 核心概念详解
3.1.1 VU(虚拟用户)
VU 是 k6 中执行压测请求的最小单元。每个 VU 独立运行一份 JavaScript 脚本副本,拥有独立的上下文(包括全局变量、模块缓存)。VU 的生命周期由 k6 运行时管理,开发者通过 export default function 定义 VU 的主执行逻辑。
| 属性 | 说明 | 典型值 |
|---|---|---|
| 并发数 | 同时运行的 VU 数量 | 10 ~ 10,000+ |
| 迭代次数 | 每个 VU 执行 default 函数的次数 |
1 ~ 无限(配合 duration) |
| 内存占用 | 每个 VU 约 1~2 MB(含 JS 运行时) | 10,000 VU ≈ 10~20 GB |
| 启动方式 | 由场景(Scenario)调度器按配置逐步或瞬时启动 | ramp-up / pause / target |
// 示例:VU 主函数定义
import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
// 每个 VU 独立执行此函数
http.get('https://test.k6.io');
sleep(1);
}
3.1.2 场景(Scenario)
场景是 k6 中定义 VU 行为模式的顶层配置单元。一个压测脚本可以包含多个场景,每个场景独立控制 VU 数量、执行时长、迭代次数和流量模型。k6 内置三种执行器(Executor),覆盖常见的压测模式。
| 执行器类型 | 适用场景 | 关键参数 |
|---|---|---|
| shared-iterations | 固定总迭代次数,VU 共享 | vus, iterations |
| per-vu-iterations | 每个 VU 执行固定迭代次数 | vus, iterations |
| constant-vus | 恒定并发,持续时长 | vus, duration |
| ramping-vus | 阶梯式增减并发 | stages: [{target, duration}] |
| externally-controlled | 由外部控制器(如 k6-operator)动态调整 | vus, duration, maxVUs |
// 示例:多场景配置
export const options = {
scenarios: {
smoke_test: {
executor: 'constant-vus',
vus: 1,
duration: '30s',
},
load_test: {
executor: 'ramping-vus',
stages: [
{ duration: '2m', target: 100 }, // 爬坡
{ duration: '5m', target: 100 }, // 稳定
{ duration: '2m', target: 0 }, // 下降
],
},
},
};
3.1.3 指标(Metrics)
k6 内置四种指标类型,用于收集压测过程中的性能数据。所有指标默认输出到终端、JSON 文件和内置的 Web Dashboard(k6 新版本)。
| 指标类型 | 内置指标示例 | 说明 |
|---|---|---|
| Counter | http_reqs, iterations, data_received | 累计计数,可计算速率 |
| Gauge | vus, vus_max | 当前瞬时值 |
| Rate | checks, failed checks | 通过/失败比率 |
| Trend | http_req_duration, http_req_waiting | 统计分布(min/max/avg/p(90)/p(95)) |
// 示例:自定义指标
import { Trend, Rate, Counter } from 'k6/metrics';
const myTrend = new Trend('api_response_time');
const myRate = new Rate('success_rate');
const myCounter = new Counter('total_orders');
export default function () {
const res = http.get('https://api.example.com/orders');
myTrend.add(res.timings.duration);
myRate.add(res.status === 200);
myCounter.add(1);
}
3.1.4 检查点(Checks)
检查点是 k6 中内置的断言机制,用于验证 HTTP 响应是否符合预期。检查点失败不会终止脚本执行,但会记录失败次数并影响 checks 和 failed checks 指标。
// 示例:检查点用法
import { check } from 'k6';
import http from 'k6/http';
export default function () {
const res = http.get('https://test.k6.io');
check(res, {
'status is 200': (r) => r.status === 200,
'response body contains welcome': (r) => r.body.includes('Welcome'),
'response time < 500ms': (r) => r.timings.duration < 500,
});
}
注意: 检查点与 JMeter 的断言(Assertion)功能类似,但 k6 的检查点默认不会中断请求流程。如需在检查点失败时停止 VU 或场景,需结合
thresholds(阈值)或自定义逻辑实现。
3.2 运行原理:事件循环与调度
k6 的运行基于 Go 语言的 事件循环(Event Loop) 和 goroutine 调度器。每个 VU 对应一个 goroutine,k6 运行时通过 场景调度器(Scenario Scheduler) 控制 VU 的创建、执行和销毁。
3.3 运行流程详解
- 脚本解析与初始化: k6 读取 JavaScript 脚本,解析
options配置(场景、阈值、指标),初始化全局模块。 - VU 创建与启动: 场景调度器根据配置的
executor和vus参数,创建对应数量的 goroutine(VU)。每个 VU 加载脚本副本,进入就绪状态。 - 迭代执行: 每个 VU 在事件循环中反复执行
export default function,直到达到迭代次数或场景时长限制。每次迭代包含:发送 HTTP 请求、执行检查点、记录自定义指标、调用sleep()等。 - 指标收集与阈值评估: 所有内置和自定义指标实时汇总到 k6 的指标引擎。在每个时间窗口结束时,k6 评估
thresholds配置,判断是否触发abortOnFail或输出告警。 - 结果输出: 压测结束后,k6 将指标数据输出到终端摘要、JSON 文件、CSV 文件或外部存储(如 InfluxDB、Prometheus)。
关键洞察: k6 的“单进程多协程”架构使其在同等硬件资源下,能够支持的并发 VU 数量远超 JMeter(JMeter 每个 VU 对应一个 Java 线程)。在 8 核 16GB 的服务器上,k6 通常可稳定运行 30,000+ VU,而 JMeter 的极限约为 5,000~10,000 线程。
3.4 核心概念关系图
以下 SVG 图展示了 VU、场景、指标、检查点之间的协作关系,以及它们如何共同构成一次完整的压测执行。
3.5 与 JMeter 架构对比
| 对比维度 | k6 | JMeter |
|---|---|---|
| 并发模型 | 单进程多协程(goroutine) | 多线程(每个 VU 一个 Java 线程) |
| 资源开销 | 低(10,000 VU ≈ 10~20 GB) | 高(10,000 VU ≈ 30~50 GB) |
| 脚本语言 | JavaScript(ES6+) | XML(GUI 配置) + Groovy/Java |
| 场景定义 | 代码内声明(options.scenarios) | GUI 线程组配置 |
| 指标收集 | 内置 4 种类型 + 自定义 | 监听器 + 聚合报告 |
| 检查点/断言 | check() 函数,不中断执行 | 断言元件,可中断线程 |
| 分布式执行 | k6-operator(Kubernetes) | Master-Slave 模式 |
选型建议: 如果团队偏向代码化、CI/CD 集成、云原生环境,k6 是更优选择;如果团队习惯 GUI 操作、需要丰富的第三方插件生态,JMeter 仍然适用。两者可以互补使用——k6 用于高频回归和巡检,JMeter 用于复杂业务逻辑模拟。