4. k6 事件驱动模型详解
k6 基于 Go 语言的协程(goroutine)和事件循环机制构建,实现了高效的异步非阻塞 I/O 模型。理解 k6 的事件驱动模型,是掌握 VU 生命周期、迭代调度和异步处理的核心前提。本章从事件循环、VU 与迭代关系、异步触发三个维度展开。
4.1 事件循环机制
k6 的每个 VU 内部运行一个独立的事件循环(Event Loop),该循环由 Go 运行时调度,但逻辑上遵循“执行 → 挂起 → 唤醒 → 执行”的异步模式。事件循环的核心组件包括:
- 任务队列(Task Queue):存放同步代码块、定时器回调、HTTP 请求完成回调等。
- 微任务队列(Microtask Queue):处理 Promise.then()、async/await 的后续操作。
- 事件轮询器(Event Poller):监听 I/O 事件(如网络响应、文件读取),就绪后推入任务队列。
关键认知:k6 的事件循环并非浏览器中的单线程模型,而是每个 VU 拥有独立的协程栈和事件循环实例,因此多个 VU 可以并行执行,互不阻塞。
4.2 VU(虚拟用户)与迭代的关系
VU 是 k6 中执行脚本的最小并发单元,每个 VU 运行一个独立的事件循环。迭代(Iteration)是 VU 执行一次 export default function 的完整过程。VU 与迭代的对应关系如下表:
| 概念 | 定义 | 生命周期 | 事件循环关联 |
|---|---|---|---|
| VU | 模拟用户,拥有独立上下文和事件循环 | 从初始化到测试结束 | 每个 VU 维护一个事件循环实例 |
| 迭代 | 一次完整的 default 函数执行 | 函数开始到返回 | 每次迭代在事件循环中排队执行 |
| VU 复用 | 同一 VU 顺序执行多次迭代 | 迭代间共享全局变量,不共享局部状态 | 迭代完成后事件循环继续等待下一轮 |
实践要点:VU 数量由
--vus 或 scenarios.vus 控制;迭代总数由 --iterations 或 duration × rate 决定。每个 VU 的迭代是串行的,但不同 VU 的迭代可以并发执行。
4.3 事件触发与异步处理
k6 的异步处理主要依赖 async/await 和 Promise 模式。事件触发场景包括:
- HTTP 请求:
http.get()返回Response对象,支持await等待响应。 - 定时器:
sleep()暂停当前迭代,事件循环让出给其他 VU。 - 自定义事件:通过
events模块或SharedArray实现 VU 间通信。 - Check/Trend 指标:异步记录性能数据,不阻塞主流程。
import http from 'k6/http';
import { sleep } from 'k6';
import { check } from 'k6';
export default async function () {
// 异步 HTTP 请求,不阻塞事件循环
let res = await http.asyncGet('https://test.k6.io');
check(res, {
'status is 200': (r) => r.status === 200,
});
// sleep 让出事件循环
sleep(1);
}
4.4 事件驱动模型最佳实践
常见误区:在 k6 中滥用同步
http.get() 会阻塞当前 VU 的事件循环,导致该 VU 无法处理其他任务。推荐使用 http.asyncGet() 或 Promise.all() 实现并发请求。
编写高效事件驱动脚本的要点:
- 优先使用异步 API:
http.asyncGet()、http.asyncPost()避免阻塞。 - 控制迭代粒度:每个迭代内不要堆积过多同步操作,保持事件循环响应。
- 合理使用 sleep:
sleep()让出 CPU,但过长的 sleep 会降低吞吐量。 - 监控事件循环延迟:通过 k6 内置指标
iteration_duration和http_req_duration判断事件循环是否过载。
// 推荐:使用 Promise.all 并发执行多个异步请求
import http from 'k6/http';
export default async function () {
const [res1, res2] = await Promise.all([
http.asyncGet('https://api1.example.com'),
http.asyncGet('https://api2.example.com'),
]);
console.log(`API1: ${res1.status}, API2: ${res2.status}`);
}
核心总结:k6 的事件驱动模型基于每个 VU 独立的事件循环,迭代在循环中串行执行,不同 VU 通过 Go 协程实现并发。异步 API 和 Promise 模式是充分利用事件循环、避免阻塞的关键手段。掌握这一模型,才能设计出高并发、低延迟的压测脚本。