k6 v0.27.0 新执行引擎深度解析:scenarios 多场景编排与七大执行器实战指南
【免费下载链接】k6A modern load testing tool, using Go and JavaScript项目地址: https://gitcode.com/GitHub_Trending/k6/k6
k6 v0.27.0 是 k6 发展史上的一个里程碑版本,其核心是历经 1.5 年开发的全新执行引擎(对应 PR #1007)。本篇文章将以该版本的官方发布说明为主体,结合当前仓库中的lib/executor等源码实现,系统讲解新执行引擎引入的 7 大执行器、scenarios多场景编排能力、优雅停止(graceful stop)语义、分布式执行分区选项,以及所有值得注意的破坏性变更,帮助你快速把旧脚本平滑迁移到新引擎,并用它建模更贴近真实流量的复杂压测场景。
为什么需要一个全新的执行引擎
在 k6 v0.27.0 之前,脚本的执行控制依赖vus、iterations、duration、stages这四个全局选项,它们相互组合的表达能力有限:
- 只能描述"固定 VU 数跑固定时长/固定迭代数"这类简单模型;
- 无法精确控制每秒发起的迭代速率(RPS);
- 无法在同一测试中并行运行多个不同形态的负载模型;
- 无法让不同场景执行不同的脚本函数。
新执行引擎把"执行形态"抽象为可组合、可并发的执行器(executor),并通过新的scenarios选项在一次测试运行中编排多个执行器。官方明确承诺:这些新场景完全是可选的,绝大多数已有脚本和选项会保持原有行为不变;全局的vus、iterations、duration、stages选项不会被弃用,它们会被透明地转换为内部某个执行器的等价配置(这一转换逻辑正是仓库中 lib/executor/execution_config_shortcuts.go 里DeriveScenariosFromShortcuts()函数所实现的)。
七大执行器全览
新引擎将已有的执行模式形式化为 4 个执行器,并新增了 3 个此前难以甚至无法建模的执行器。所有执行器(除externally-controlled外)均可同时用于本地k6 run和云端k6 cloud分布式执行——包括此前云端不支持的shared-iterations,因此现在可以直接执行k6 cloud --iterations 10000 --vus 100 script.js。
4 个形式化自旧选项的执行器
shared-iterations:共享迭代数固定数量的迭代被所有 VU 共享,全部迭代执行完毕后测试结束。它等价于全局vus+iterations(可选加duration)。从源码看,其默认配置为VUs: 1、Iterations: 1、MaxDuration: 10 * time.Minute(见 lib/executor/shared_iterations.go),并且校验要求迭代数不能小于 VU 数。
constant-vus:固定 VU 数固定数量的 VU 在指定时长内尽可能多地执行迭代。等价于全局vus+duration。
ramping-vus:阶梯式 VU 数VU 数量随时间按stages阶梯变化。等价于全局stages选项。
externally-controlled:外部控制运行时通过 k6 的 REST API 或 CLI(即k6 scale、k6 pause等命令)动态控制和伸缩执行规模。它是唯一支持无限时长测试的执行器,也是唯一可在测试启动后再次暂停的执行器。
3 个全新的执行器
per-vu-iterations:每个 VU 固定迭代数每个 VU 各自执行固定数量的迭代(对应 issue #381)。常用于"每个虚拟用户完成 N 轮业务"的建模。从源码看,它同样有maxDuration上限(默认 10 分钟),且迭代数是按 VU 计而非共享的(见 lib/executor/per_vu_iterations.go,其注释特别强调迭代数不做缩放,以避免分布式分区时产生二次方效应)。
constant-arrival-rate:恒定到达速率以指定的固定速率持续启动迭代,持续指定时长。k6 会在测试运行中动态调整活跃 VU 数量,以满足"每个时间周期内指定数量的迭代"。这对精确表达 RPS(每秒请求数)非常有价值(对应 issue #550)。其关键配置项Rate、TimeUnit、Duration、PreAllocatedVUs、MaxVUs的校验逻辑可见 lib/executor/constant_arrival_rate.go:速率必须大于 0,timeUnit必须大于 0,duration必须大于minDuration,maxVUs不能小于preAllocatedVUs。
ramping-arrival-rate:阶梯式到达速率在指定时间段内执行可变数量的迭代。与ramping-vus类似使用stages,但阶段的目标不是 VU 数量,而是每秒应执行的迭代数。适合模拟"峰值时刻 RPS 爬升、随后回落"的真实流量曲线。
执行器配置速查表
| 执行器 | 核心配置项 | 适用场景 |
|---|---|---|
shared-iterations | vus、iterations、maxDuration | 固定总量、尽快完成的任务队列 |
constant-vus | vus、duration | 恒定并发用户数的压测 |
ramping-vus | startVUs、stages、gracefulRampDown | 模拟用户数阶段性增减 |
externally-controlled | vus、maxVUs、duration | 需在运行中手动/API 伸缩 |
per-vu-iterations | vus、iterations、maxDuration | 每个用户固定轮次的业务 |
constant-arrival-rate | rate、timeUnit、duration、preAllocatedVUs、maxVUs | 精确控制 RPS |
ramping-arrival-rate | startRate、timeUnit、stages、preAllocatedVUs、maxVUs | 模拟真实流量峰谷 |
用 scenarios 编排多场景测试
新引擎的核心入口是scenarios选项。它允许在一次测试中配置多个执行场景:这些场景可以顺序运行,也可以并行运行,并且彼此独立——可以执行不同的脚本函数、使用不同的执行器类型和选项、设置各自的环境变量与指标标签。
完整示例:三个并行场景
发布说明中给出的完整示例(在options中同时配置了 Web 测试、恒定速率 API 测试和阶梯速率 API 测试):
import http from 'k6/http'; import { sleep } from 'k6'; export let options = { scenarios: { my_web_test: { // some arbitrary scenario name executor: 'constant-vus', vus: 50, duration: '5m', gracefulStop: '0s', // do not wait for iterations to finish in the end tags: { test_type: 'website' }, // extra tags for the metrics generated by this scenario exec: 'webtest', // the function this scenario will execute }, my_api_test_1: { executor: 'constant-arrival-rate', rate: 90, timeUnit: '1m', // 90 iterations per minute, i.e. 1.5 RPS duration: '5m', preAllocatedVUs: 10, // the size of the VU (i.e. worker) pool for this scenario maxVUs: 10, // we don't want to allocate more VUs mid-test in this scenario tags: { test_type: 'api' }, // different extra metric tags for this scenario env: { MY_CROC_ID: '1' }, // and we can specify extra environment variables as well! exec: 'apitest', // this scenario is executing different code than the one above! }, my_api_test_2: { executor: 'ramping-arrival-rate', startTime: '30s', // the ramping API test starts a little later startRate: 50, timeUnit: '1s', // we start at 50 iterations per second stages: [ { target: 200, duration: '30s' }, // go from 50 to 200 iters/s in the first 30 seconds { target: 200, duration: '3m30s' }, // hold at 200 iters/s for 3.5 minutes { target: 0, duration: '30s' }, // ramp down back to 0 iters/s over the last 30 second ], preAllocatedVUs: 50, // how large the initial pool of VUs would be maxVUs: 100, // if the preAllocatedVUs are not enough, we can initialize more tags: { test_type: 'api' }, // different extra metric tags for this scenario env: { MY_CROC_ID: '2' }, // same function, different environment variables exec: 'apitest', // same function as the scenario above, but with different env vars }, }, discardResponseBodies: true, thresholds: { // we can set different thresholds for the different scenarios because // of the extra metric tags we set! 'http_req_duration{test_type:api}': ['p(95)<250', 'p(99)<350'], 'http_req_duration{test_type:website}': ['p(99)<500'], // we can reference the scenario names as well 'http_req_duration{scenario:my_api_test_2}': ['p(99)<300'], } }; export function webtest() { http.get('https://test.k6.io/contacts.php'); sleep(Math.random() * 2); } export function apitest() { http.get(`https://test-api.k6.io/public/crocodiles/${__ENV.MY_CROC_ID}/`); // no need for sleep() here, the iteration pacing will be controlled by the // arrival-rate executors above! }这个例子同时展示了多个关键能力:
- 阈值按场景隔离:由于每个场景设置了不同的
tags,阈值可以写成http_req_duration{test_type:api}或http_req_duration{scenario:my_api_test_2},针对不同场景施加不同的 SLA; - 不同函数、不同环境变量:
my_api_test_1与my_api_test_2执行同一个apitest函数,但env中的MY_CROC_ID不同,实现了"同一段代码、不同输入"的复用; - 到达速率场景无需
sleep():迭代节奏完全由执行器按速率控制,脚本中不应再手动加sleep。
关于场景命名,源码中有明确的约束:场景名只能包含数字、拉丁字母、下划线和短横线(正则^[0-9a-zA-Z_-]+$,见 lib/executor/base_config.go)。另外,如果脚本没有显式指定任何执行配置,DeriveScenariosFromShortcuts()会默认生成一个per-vu-iterations配置:1 个 VU、1 次迭代(见 lib/executor/execution_config_shortcuts.go)。
执行器公共选项:startTime、gracefulStop、exec、env、tags
所有执行器共享一组公共配置,定义在 lib/executor/base_config.go 的BaseConfig中:
startTime:定义该场景相对整个测试开始时刻的启动时间,用于让不同场景错峰启动。校验要求其值不能为负(见 lib/executor/base_config.go)。gracefulStop:允许迭代在正常执行时长结束后再优雅地运行一段时间,让进行中的迭代自然收尾,而不是被立即打断。源码中该选项的默认值是DefaultGracefulStopValue = 30 * time.Second(见 lib/executor/base_config.go)。ramping-vus额外拥有gracefulRampDown,用于在 VU 阶梯下降时给迭代留出收尾时间,其默认值同样是 30 秒(见 lib/executor/ramping_vus.go)。如需恢复旧版"立即中断"的行为,把这两个选项显式设为'0s'即可。exec:指定该场景要执行的脚本函数名。默认为default(常量consts.DefaultFn),未显式指定时即执行脚本默认导出的default函数(见 lib/executor/base_config.go)。这让"构建测试套件"成为可能:一个脚本文件导出多个函数,由不同场景分别执行。env:为场景设置专属环境变量,脚本中通过__ENV读取,便于代码复用与数据注入。tags:为场景产生的所有指标附加额外标签,是"按场景设置阈值、按场景聚合报表"的基础。
新指标 dropped_iterations:配置是否合理的信号灯
新引擎引入了一个内置指标dropped_iterations(计数器类型,定义于 metrics/builtin.go)。当 k6 无法按时执行一次迭代时,该指标会计数一次,可能出现在shared-iterations、per-vu-iterations、constant-arrival-rate、ramping-arrival-rate这四类执行器中:
- 对到达速率类执行器,取决于配置的速率是否超出实际执行能力;
- 对迭代数类执行器,取决于是否触碰了场景的
maxDuration上限。
因此,dropped_iterations增长通常是配置不合理或被测系统过载的明确信号,应当作为压测结果检查的重点指标。
用 execution-segment 分区测试运行
为了支持将一次测试运行切分到多个 k6 实例并行执行,v0.27.0 新增了--execution-segment和--execution-segment-sequence两个选项。它们在底层对应 lib/execution_segment.go 中的ExecutionSegment/ExecutionSegmentSequence类型,其Scale方法会在配置校验时按比例缩放执行器的 VU 数、迭代数等参数(lib/execution_segment.go)。该能力最初应用于测试执行层面(所有新执行器类型都支持),并为未来"测试数据分区"等长期需求打开了大门。
UX 与修复:更清晰的运行体验
v0.27.0 在用户体验上有几项值得注意的改进:
- CLI 进度条:每个执行器拥有独立的描述文本与实时线程安全进度条,多场景并行时各自的进度一目了然;
__VU可在 init 上下文使用:脚本在初始化阶段即可读取__VU,方便按 VU 切分测试输入数据、降低内存占用(issue #889);- REST API 停止执行:新增通过 REST API 停止引擎执行的方法(issue #1352);
- 模块导入错误信息:改进了 JS 模块导入失败时的报错提示。
Bug 修复覆盖 CLI(--http-debug不再因 dump 错误退出、JSON 输出噪音降低、iterations与stages混用不再导致退出、summary 中 check 计数不一致)、配置(更严格的stages校验)、JS 运行时(goja 罕见 panic)、HTTP(请求超时与指标上报上下文错误)、执行调度(快速 ramp 时偶发context cancelled跳过迭代)以及 WebSocket(连接挂起、goroutine 泄漏、指标提前上报)等若干问题。
内部实现:一次接近重写的架构变更
PR #1007 几乎是 k6 执行调度部分的完整重写,涉及测试运行执行方式的深层架构变化,并使整体代码覆盖率提升约 2%。与此同时:
- 构建与测试切换到Go 1.14,带来若干修复与性能提升;
- WebSocket 库升级为更新的
gorilla/websocket,带来小幅性能优化; - 进行了一轮代码清理与格式化。
这些基础设施在今天的仓库中仍能看到影子:执行器配置统一注册在 lib/executor 目录下,每个执行器文件通过init()中的lib.RegisterExecutorConfigType()完成类型注册(如 lib/executor/constant_arrival_rate.go)。
Breaking Changes:升级前必须知道的事
v0.27.0 引入了若干破坏性变更,升级脚本时务必逐条核对:
1. 上层配置覆盖下层执行配置执行配置选项(scenarios、stages、iterations、duration)在配置合并时遵循"上层覆盖下层"规则:CLI 参数 > 环境变量 > JS 脚本选项 > JSON 配置文件。例如--iterations会覆盖环境变量(K6_DURATION、K6_STAGES等)或脚本中的执行配置。这一逻辑在 lib/options.go 的Options.Apply()中有清晰实现:只要高层指定了duration/iterations/stages/scenarios中的任意一个,就会清空低层的全部执行设置。
2. shared-iterations 默认 10 分钟超时此前如果脚本设置了iterations和vus但未设置duration,理论上可能无限运行(这也是旧引擎下该模式不允许用于k6 cloud的原因之一)。从 v0.27.0 起,若指定迭代未在 10 分钟内完成,脚本将中止——这是shared-iterations执行器的默认maxDuration值。可通过场景内的maxDuration修改,或使用快捷选项duration/--duration/K6_DURATION调整。
3. 默认 30 秒优雅停止此前所有迭代都是可中断的:duration一到或 VU 阶梯下降立即打断运行中的迭代。现在除externally-controlled外所有执行器默认有 30 秒的gracefulStop收尾期(ramping-vus另有gracefulRampDown)。收尾期内执行器不再启动新迭代,但允许正在运行的迭代在期限内自然完成。
4. 同层配置冲突将直接报错同一配置层上同时使用多个执行选项现在是配置冲突错误,会中止脚本。例如k6 run --duration 10s --stages 5s:20 script.js将无法运行。唯一例外是duration与iterations的组合,它会被解析为带自定义maxDuration的shared-iterations执行器。
5. REST API 控制仅限 externally-controlledk6 pause、k6 scale等控制命令现在只在配置了externally-controlled执行器时生效。k6 run --paused script.js的启动暂停仍适用于所有执行器,但k6 resume之后,除非使用externally-controlled,否则无法再次暂停。
6. --paused 在 setup() 之前生效旧行为是执行完setup()后才暂停;现在 k6 会在执行setup()之前就进入暂停状态。
7. __VU 不再因 ramp 递增此前通过stages反复 ramp down/up 时,__VU全局变量会在每次 ramp up 时递增。现在不再如此:整个测试运行中__VU的最大值永远不会超过已初始化的 VU 总数。
8. vusMax 弃用vusMax/K6_VUS_MAX/-m/--max选项被弃用。它此前用于通过 REST API 控制已初始化 VU 数;由于该能力已收窄到externally-controlled执行器,其对应选项更名为maxVUs。
9. 无限时长仅限 externally-controlled无限时长的测试现在只能通过externally-controlled执行器实现。
10. --address 启动失败将报错退出指定了--address但 API 服务器无法启动时,k6 会以错误退出,而不再只是警告。
11. toUTCString() 输出格式修正Date.prototype.toUTCString()的返回值从Thu Jan 01 1970 00:00:00 GMT+0000 (UTC)修正为符合 ECMAScript 规范的Thu, 01 Jan 1970 00:00:00 GMT。
12. setup/teardown 超时默认值调整setupTimeout与teardownTimeout的默认值从 10 秒改为 60 秒(见 lib/options.go 中对应的K6_SETUP_TIMEOUT/K6_TEARDOWN_TIMEOUT配置项)。
迁移建议
- 简单脚本无需改动:只使用
vus+duration/iterations/stages的脚本会透明地映射到对应执行器,行为基本不变,只需留意上述 breaking changes(尤其是第 2、3、4 条)。 - 精确控制 RPS:从"固定 VU 数 +
sleep()"估算速率,迁移到constant-arrival-rate或ramping-arrival-rate,让引擎替你管理 VU 池,脚本中删掉sleep()。 - 构建复杂压测套件:用
scenarios+exec+env+tags组织多函数、多负载模型、多 SLA 阈值的组合测试。 - 多机横向扩展:借助
--execution-segment/--execution-segment-sequence将一次运行切分到多台机器。
新执行引擎从 v0.27.0 起成为 k6 执行调度的基石,其架构(执行器注册、配置校验、VU 池动态分配、优雅停止)沿用至今,理解它有助于你写出更贴近真实流量、更可控、更可观测的压测脚本。
【免费下载链接】k6A modern load testing tool, using Go and JavaScript项目地址: https://gitcode.com/GitHub_Trending/k6/k6
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考