如何结合 k6 进行性能测试
k6 是什么
k6 是 Grafana 开源的性能测试工具,特点:
- 用 JavaScript/TypeScript 写脚本。
- CLI 执行,适合 CI。
- 支持多种压测场景:固定并发、阶梯加压、固定到达率、峰值、浸泡。
- 内置阈值(thresholds)、检查(checks)和指标。
- 可输出 JSON、CSV、Prometheus、Grafana Cloud 等。
适合:接口压测、微服务容量评估、CI 性能门禁、持续性能测试。
安装和运行
# Windows: winget install k6 --source winget
# macOS: brew install k6
# 或直接下载二进制
k6 run script.js
k6 run --vus 50 --duration 5m script.js
脚本结构
import http from 'k6/http'
import { check, sleep } from 'k6'
import { Trend, Rate } from 'k6/metrics'
const loginDuration = new Trend('login_duration')
const orderFailRate = new Rate('order_fail_rate')
export const options = {
scenarios: {
smoke: {
executor: 'constant-vus',
vus: 5,
duration: '1m',
tags: { test_type: 'smoke' },
},
load: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 50 },
{ duration: '5m', target: 50 },
{ duration: '2m', target: 0 },
],
startTime: '1m',
tags: { test_type: 'load' },
},
},
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
'http_req_duration{test_type:load}': ['p(95)<800'],
},
}
export default function () {
const loginRes = http.post(`${__ENV.BASE_URL}/api/login`, JSON.stringify({
username: __ENV.USER,
password: __ENV.PASSWORD,
}), { headers: { 'Content-Type': 'application/json' } })
loginDuration.add(loginRes.timings.duration)
check(loginRes, {
'login 200': (r) => r.status === 200,
'has token': (r) => !!r.json('token'),
})
const token = loginRes.json('token')
const headers = { Authorization: `Bearer ${token}` }
const products = http.get(`${__ENV.BASE_URL}/api/products`, { headers })
check(products, { 'products 200': (r) => r.status === 200 })
const order = http.post(`${__ENV.BASE_URL}/api/orders`, JSON.stringify({
productId: 101,
quantity: 1,
}), { headers: { ...headers, 'Content-Type': 'application/json' } })
orderFailRate.add(order.status >= 400)
check(order, {
'order 200': (r) => r.status === 200,
'order has id': (r) => !!r.json('order.id'),
})
sleep(1)
}
场景类型
| 场景 | executor | 适用 |
|---|---|---|
| 冒烟 | constant-vus | 验证脚本和基本链路 |
| 负载 | ramping-vus | 模拟逐步加压和稳态 |
| 固定到达率 | constant-arrival-rate | 精确控制 RPS |
| 阶梯到达率 | ramping-arrival-rate | 找拐点、测容量 |
| 峰值 | ramping-arrival-rate | 短时突发 |
| 浸泡 | constant-vus + 长时间 | 内存泄漏、连接泄漏 |
| 按迭代 | per-vu-iterations / shared-iterations | 固定次数 |
工作负载建模
压测不是“把 VU 拉满”,而是模拟真实业务:
- 业务比例:登录、浏览、加购、下单、支付各占多少。
- 思考时间:用
sleep模拟用户间隔。 - 数据分布:热门商品和冷门商品比例。
- 峰值模型:日常、活动、大促的流量曲线。
- 持续时间:短时峰值和长时间浸泡都要覆盖。
- 环境隔离:压测环境独立,避免影响测试数据和其他人。
数据和关联
- 用
SharedArray加载 CSV/JSON,避免每个 VU 复制大对象。 - 每个 VU 使用唯一用户和数据,避免锁竞争和重复数据。
- 从登录响应提取 token,从创建订单响应提取 orderId。
- 用
http.batch并行请求,模拟页面并发加载。 - 需要签名时,在脚本里实现签名函数,注意时间戳和 nonce。
阈值和检查
- checks:断言单次请求结果,失败会计入指标。
- thresholds:定义通过/失败标准,CI 中可直接阻断。
- 常用阈值:
http_req_duration: ['p(95)<500']http_req_failed: ['rate<0.01']- 自定义指标,如
order_fail_rate: ['rate<0.001']
结果输出
k6 run --out json=result.json script.js
k6 run --out csv=result.csv script.js
- 本地分析:JSON/CSV。
- 实时监控:Prometheus remote write + Grafana。
- 云端:Grafana Cloud k6。
- CI:阈值失败返回非零退出码,阻断流水线。
分布式压测
单机压测容易受客户端 CPU、网络和端口限制。流量大时:
- 多台压测机分片。
- Kubernetes 上用 k6 Operator。
- 压测机和服务端分开,避免互相影响。
- 监控压测机自身的 CPU、网络和连接数。
最佳实践
- 先冒烟,再负载,再峰值/浸泡。
- 每次只改变一个变量,便于对比。
- 记录版本、配置、数据量和环境。
- 同时监控服务端、数据库、缓存、MQ 和网络。
- 压测数据要清理或使用独立环境。
- 把核心接口的性能阈值接入 CI,防止性能回退。
反模式
- 只压一个接口,忽略业务链路。
- 所有 VU 共用同一个账号,产生锁竞争。
- 压测机和服务端在同一台机器。
- 只看平均值,不看 p95/p99。
- 没有基线,无法判断变好还是变坏。
- 压测后不清理数据,污染环境。
面试回答模板
我会用 k6 写接口压测脚本,按冒烟、负载、峰值和浸泡设计场景,用 ramping-vus 或 arrival-rate 模拟真实流量。脚本里做登录 token 关联、唯一数据、业务比例和思考时间,设置 p95/p99 和错误率阈值,接入 CI 做性能门禁。结果输出到 JSON/Prometheus,同时监控服务端、数据库和缓存,最后结合服务端指标定位瓶颈。