ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

k6 脚本一次写对:从零跑通性能压测

2026/9/3 12:55:22 拓冰建站 浏览量
k6 脚本一次写对:从零跑通性能压测 k6 脚本一次写对从零跑通性能压测【免费下载链接】k6A modern load testing tool, using Go and JavaScript项目地址: https://gitcode.com/GitHub_Trending/k6/k6压测跑到第 5 分钟终端被 500 错误刷了满屏结果里你设的错误率居然还是“通过”——阈值没挂在正确的指标名上。k6 是一个用 JavaScript 脚本驱动负载测试的工具这类坑跟特性熟不熟无关是脚本没当成一个整体来写。三行代码先让 k6 跑起来你就想先确认目标服务通不通结果卡在脚本第一行二十分钟。别急着写业务先把这个三段式跑通import http from k6/http; export function setup() { return { token: demo-token }; // 测试前只跑一次 } export default function (data) { // 每轮迭代都执行压测的主体 http.get(https://test-api.example.com/items, { headers: { Authorization: Bearer ${data.token} } }); } export function teardown(data) { console.log(run finished); // 测试后只跑一次 }为什么是这三个导出setup 做一次性准备default 是每轮迭代的真实流量teardown 收尾清理。k6 靠这三块就能跑其余都是装饰。能跑了问题来了你打算把 20 个接口和一串辅助函数全塞进这同一个文件。脚本写长了怎么拆k6 模块拆分脚本涨到 300 行、8 个接口拼 header 的逻辑重复了四处改一个字段像拆炸弹。拆不拆的判断标准其实就两条两个接口共用同一段逻辑或者单文件超过 100 行满足任一条就拆。来源写法典型例子内置模块import http from k6/httphttp、metrics、data本地 .jsimport { genUser } from ./utils.js业务辅助函数、参数拼装外部扩展import redis from k6/x/redisk6/x/redis 等构建期编译进二进制把共用逻辑抽进一个 utils.js// utils.js多个接口共用 export function genUser() { return { name: user_${Math.random().toString(36).slice(2)}, age: 18 }; } export const headers { Content-Type: application/json };主脚本里一句import { genUser, headers } from ./utils.js之后只关心请求本身。文件拆好了可流程不串起来数据还是接不上真人用户不是一个 GET 就完事的。像真人一样点按钮你的测试全是绿的 GET但真人会先登录、再改、再读、最后登出每一步的数据要递给下一步。四步串成一条链公共请求参数放进一个对象里复用import http from k6/http; const base https://api.example.com; const params { // 四个请求共用 headers: { Authorization: Bearer ${__ENV.TOKEN} }, timeout: 10s, tags: { scenario: edit-flow }, }; export default function () { const login http.post(${base}/login, { user: a, pass: b }, params); const id JSON.parse(login.body).id; // 把数据递给下一步 http.put(${base}/users/${id}, { name: 新名字 }, params); const got http.get(${base}/users/${id}, params); http.del(${base}/logout, null, params); }循环里每个请求都走同一个节奏校验就两个动作check()管壳状态码、耗时res.json()管瓤业务字段import { check } from k6; check(got, { 状态码 200: (r) r.status 200, 耗时 500ms: (r) r.timings.duration 500, }); const name got.json().name; // 把 body 当 JSON 解析 check(name, { 字段非空: (v) !!v });流程齐了可压测一结束用什么判断过没过光看终端刷得飞不算数。告诉 k6 什么算达标阈值与场景编排跑完所有人问“过没过”你只能说“看着挺快”——达标线必须写进脚本否则 k6 只能告诉你“测完了”不能告诉你“达标了”。k6 阈值怎么写八成场景就两类表达式响应时间用p(95)错误率用rate。export const options { scenarios: { stable: { executor: constant-vus, vus: 10, duration: 10m }, // 恒定 VU ramp: { executor: ramping-vus, startVUs: 10, stages: [ // 阶梯加压 { duration: 5m, target: 100 }, { duration: 3m, target: 100 }, { duration: 1m, target: 0 }, ] }, }, thresholds: { http_req_duration: [p(95)500], // 95% 的请求 500ms http_req_failed: [rate0.01], // 错误率 1% }, };两种执行器放一起对比选型不纠结执行器VU 行为适用场景constant-vus人数钉死不动稳态浸泡跑满 10 分钟看劣化ramping-vus按 stages 台阶升降找峰值、找拐点阈值和场景在同一个options里改负载不用动校验逻辑。仓库里还有现成例子可抄examples/thresholds.js、examples/stages.js。阈值定了、场景排了可 VU 一上量一行await漏写整场压测就乱了。让并发真正跑起来k6 异步与 Promise并发一上量终端开始刷“token undefined”——这不是并发的问题是时序的问题。嵌套回调不顺手的话直接改写成 async/await// 老式 .then()每跳一层多一层嵌套 http.get(url).then((r) r.json()).then((d) use(d)); // async/await从上往下读一个 catch 兜住所有错误 const d await (await http.get(url)).json(); use(d);服务不稳时两个小函数能救命import { sleep } from k6; async function withTimeout(p, ms) { // 3 秒没结果就放弃 return Promise.race([p, new Promise((_, rej) setTimeout(() rej(new Error(timeout)), ms))]); } async function retry(fn) { // 最多 3 次1s/2s 退避 for (let i 1; ; i) { try { return await fn(); } catch { if (i 3) throw new Error(retry exhausted); await sleep(i); } } }一句话分清sleep(1)是真等 1 秒、会算进测试时长await一个请求只是等这次请求的结果前者是时间后者是依赖。脚本全写完了按下回车前的最后 5 分钟值得把清单翻一遍。压测前 5 分钟自检终端已经预热好了发车前 30 秒对照这几条阈值设了吗http_req_duration和http_req_failed至少各挂一条阈值破了要不要中止全场要的话用对象形式阈值加abortOnFail: true请求异常被 try/catch 兜住了没兜住的错误只会在汇总里变成 VU 失败率突变场景时长 ≥ 5 分钟30 秒的数据百分位数没有统计意义token 走__ENV而不是硬编码⚠️ 脚本会进版本库ramping-vus 降载后留了gracefulRampDown让在途请求跑完先用 1 个 VU 冒烟流程完整走通一次再放大 VU 数清单过完再回车。这次跑完的脚本下次换个环境直接复用。【免费下载链接】k6A modern load testing tool, using Go and JavaScript项目地址: https://gitcode.com/GitHub_Trending/k6/k6创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考