news 2026/9/3 12:54:54

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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/http'http、metrics、data
本地 .jsimport { genUser } from './utils.js'业务辅助函数、参数拼装
外部扩展import redis from 'k6/x/redis'k6/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: ['rate<0.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_durationhttp_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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 12:54:28

基于改进VSG的三相逆变器快速预同步控制与Simulink仿真

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 12:53:48

三星为英伟达定制8Hi HBM,17~18Gbps速率意味着什么?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 12:53:42

专业运动体能馆创业指南:从市场调研到运营管理的完整实操手册

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 12:51:57

从儿童自行车刷PB到性能基准:识别伪纪录与数据噪声

陪儿子在小区封闭环形道上练车时&#xff0c;我图省事&#xff0c;顺手跨上他那辆前阵子才换的儿童自行车&#xff0c;小小地骑了两圈。车型偏小&#xff0c;坐垫调到最高也还是委屈&#xff0c;蹬起来有点别扭。当天我只是当娱乐&#xff0c;没当回事。晚上同步运动手表&#…

作者头像 李华
网站建设 2026/9/3 12:45:41

从蔬菜检测数据集到实用模型:目标检测全链路实战指南

简介&#xff1a;本资源为面向计算机视觉初学者与算法工程师的蔬菜目标检测专用数据集&#xff0c;适用于YOLO、Faster R-CNN等主流目标检测模型的训练与验证&#xff0c;可支撑农业智能分拣、生鲜零售识别、智慧食堂菜品分析等实际场景开发。压缩包共842个文件&#xff0c;含4…

作者头像 李华
网站建设 2026/9/3 12:43:10

【单片机毕设案例分享】阈值自定义的 STM32 人体健康监测报警装置研究 基于 STM32 MPU6050 的跌倒检测与运动信息采集系统(013306)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于单片机&#xff0c;STM32单片机&#xff0c;51单片机&#xff0c;J…

作者头像 李华