性能测试工具-k6
一、k6是什么
k6 是用 Go 语言编写的一种高性能的负载测试工具。js脚本写用例的压测工具,简单、快速、报告详细具有下面几个特点。
- K6 嵌入了 JavaScript 运行时,可以使用 JavaScript ES2015/ES6 来编写脚本。
- 强大的 CLI 工具。
- 使用 Checks 和 Thresholds 可以更加轻松的做面向目标的自动化的负载测试。
K6 相对于 JMeter 的优势
- K6 是 Go 编写的,相对于 JAVA 编写的 JMeter 有性能上的优势,K6 可以只用较少的资源就能达到指定数量的负载。
- 支持阈值。
- Javascript 的脚本可以更好的促进协作和版本管理。
- 资源利用率远远强于 JMeter。
- 丰富的可视化方案。
选择 k6,不是因为它“取代”了 JMeter,而是因为它解决了 JMeter 在“开发者工作流”中的痛点:
- 脚本难维护 → 用 JS 写,Git 友好
- 资源消耗大 → 轻量高效
- CI 集成麻烦 → 命令行原生支持
- 监控复杂 → 一键对接 Grafana
如果你也厌倦了 JMeter 的笨重配置,k6 能让你用写代码的方式,5 分钟完成一次高并发压测。
二、使用方法
1. 安装
windows:
choco install k6
Debian/Ubuntu:
sudo apt-get update && sudo apt-get install ca-certificates gnupg2 -y
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
Docker:
docker pull grafana/k6
核心概念
- VU(Virtual User):虚拟用户,模拟真实用户行为。
- Iteration:每个 VU 执行一次 default 函数即一次迭代。
- Options:并发数、持续时间、阈值、阶段(stages)。
- Checks:断言,不影响请求但计入通过率。
- Thresholds:性能阈值(如 P95 < 200ms),违反则测试失败。
2、示例
新建一个 test.js 文件
Get 请求 get( url, [params] )
import http from 'k6/http';
export let options = {
vus: 100, // 指定要同时运行的虚拟用户数量
duration: '10s', // 指定测试运行的总持续时间
};
// default 默认函数
export default function () {
// 标头
let params = { headers: { 'Content-Type': 'application/json' } };
var res=http.get("https://test.k6.io",params)
}
引入html报告的写法
import http from 'k6/http'; // 引入 k6-reporter 的 htmlReport(k6 支持从 URL 直接 import 模块,无需 npm install)
//也可以直接将该bundle.js下载下来,使用时import本地地址即可,省去运行时在下载的问题 import { htmlReport } from 'https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js'; export let options = { vus: 100, // 指定要同时运行的虚拟用户数量 duration: '10s', // 指定测试运行的总持续时间 }; // default 默认函数 export default function () { // 标头 let params = { headers: { 'Content-Type': 'application/json' } }; var res=http.get("https://test.k6.io",params) } // 运行结束后自动生成 HTML 报告(用浏览器打开 summary.html 即可查看) export function handleSummary(data) { return { 'summary.html': htmlReport(data), 'summary.json': JSON.stringify(data), // 可选:同时输出 JSON 格式结果 }; }
Post 请求 Post( url, [body],[params])
import http from 'k6/http';
export let options = {
vus: 100,
duration: '10s',
};
// default 默认函数
export default function () {
// json 字符串
let json = { content: 'linhui', image: 'images' };
// 标头
let params = { headers: { 'Content-Type': 'application/json' } };
var res = http.post("https://host/api/feedback", JSON.stringify(json), params)
console.log(res.status);
}
del 请求 del( url,[body],[params])
import http from 'k6/http';
export let options = {
vus: 1,
duration: '10s',
};
// default 默认函数
export default function () {
let json = {id:1};
let params = { headers: { 'Content-Type': 'application/json' } };
http.del('https://host/delete', json, params);
}
batch 批处理,可以用来做页面并发,批处理并不能保证执行顺序,batch(method,url,[body],[params])
import http from 'k6/http';
export let options = {
vus: 1,
duration: '10s',
};
export default function () {
let get1 = {
method: 'GET',
url: 'https://host/get',
};
let get2 = {
method: 'GET',
url: 'https://host/get',
};
let post = {
method: 'POST',
url: 'https://host/post',
body: {
hello: 'world!',
},
params: {
headers: { 'Content-Type': 'application/json' },
},
};
let res = http.batch([get1, get2, post]);
}
request 发送求 request( method, url, [body], [params])
import http from 'k6/http';
export let options = {
vus: 1,
duration: '10s',
};
export default function () {
let json = { content: 'linhui', image: 'images' };
let params = { headers: { 'Content-Type': 'application/json' } };
let res = http.request('POST', 'http://host/post', JSON.stringify(json), params);
let res1 = http.request('GET', 'http://host/get', null, params);
}
执行脚本,进入脚本根目录
k6 run test.js
# 使用 docker
docker run -i grafana/k6 run - <test.js
未引入html的脚本执行后生成:
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: .\stresstest.js
output: -
scenarios: (100.00%) 1 scenario, 10 max VUs, 1m30s max duration (incl. graceful stop):
* default: 10 looping VUs for 1m0s (gracefulStop: 30s)
running (1m00.0s), 00/10 VUs, 25657 complete and 0 interrupted iterations
default ✓ [======================================] 10 VUs 1m0s
✗ is status 400
↳ 0% — ✓ 0 / ✗ 25657
checks.........................: 0.00% ✓ 0 ✗ 25657
data_received..................: 4.3 MB 72 kB/s
data_sent......................: 2.2 MB 37 kB/s
http_req_blocked...............: avg=4.46µs min=0s med=0s max=7.84ms p(90)=0s p(95)=0s
http_req_connecting............: avg=273ns min=0s med=0s max=1ms p(90)=0s p(95)=0s
http_req_duration..............: avg=22.99ms min=0s med=21.14ms max=99.26ms p(90)=34.6ms p(95)=35.91ms
{ expected_response:true }...: avg=22.99ms min=0s med=21.14ms max=99.26ms p(90)=34.6ms p(95)=35.91ms
http_req_failed................: 0.00% ✓ 0 ✗ 25657
http_req_receiving.............: avg=74.07µs min=0s med=0s max=5.34ms p(90)=249.16µs p(95)=608.73µs
http_req_sending...............: avg=11.74µs min=0s med=0s max=3.24ms p(90)=0s p(95)=0s
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=22.9ms min=0s med=21.03ms max=99.26ms p(90)=34.5ms p(95)=35.83ms
http_reqs......................: 25657 427.448837/s
iteration_duration.............: avg=23.38ms min=0s med=21.63ms max=111.07ms p(90)=34.98ms p(95)=36.32ms
iterations.....................: 25657 427.448837/s
vus............................: 10 min=10 max=10
vus_max........................: 10 min=10 max=10
引入html的脚本执行后除了生成上面的终端文字报告还可生成 summary.html (用浏览器打开 HTML 即可)。
- http_reqs:总请求与速率(QPS),衡量吞吐。
- http_req_duration:总时延(p50/p90/p95);p95 是体验目标的主指标。
- http_req_waiting:TTFB(服务端处理主耗时);若≈duration,瓶颈在服务端。
- http_req_failed / checks:错误率与断言通过率;理想≈0%、≈100%。
- 被违反阈值(breached thresholds):性能目标未达标的直接证据。
- 趋势曲线:随阶段变化的延迟与吞吐,拐点暴露瓶颈(连接池/线程/队列/GC)。
三、参数说明
1、概览
k6 里最容易混的是把下面 5 类当成一回事。它们性质不同,必须分开看:
|
名称 |
它回答的问题 |
本质 |
|---|---|---|
|
Metrics(指标) |
测试结果量化成什么数据 |
数值度量,本文主角 |
|
Options(配置) |
这次测试怎么跑 |
运行参数(并发/时长/阶段…) |
|
Checks(断言) |
响应"对不对" |
功能正确性校验,不中断脚本 |
|
Thresholds(阈值) |
性能"达不达标" |
判据,违反即测试失败 |
|
Logs(日志) |
跑的时候看什么 |
调试输出,不在指标体系内 |
Metrics 是核心数据;
Options(配置)决定它怎么产生; — 通过 export let options = {...} 设定,决定指标如何产生。常用项:
-
vus/duration:并发用户数 / 测试时长 -
stages:分阶段爬坡(如 100→500→0) -
rps:限制每秒请求数(限流) -
thresholds:性能阈值(见下) -
tags:给请求打标签 -
discardResponseBodies: true:丢弃响应体,降内存与 GC 压力,生产必开 -
noConnectionReuse/batch/blacklistIPs等:网络行为控制
Checks(断言) 是对它的校验,"响应对不对"(功能断言,不中断);— 在脚本内写 check(res, {...}) 校验响应,失败不中断脚本,只把通过率计入 checks 指标。用于"功能对不对"。
Thresholds(阈值)是对它的校验,"性能达不达标"(性能判据,可中断);— 写在 options.thresholds,引用某个指标(含 checks)作为"达不达标"的判据。违反即测试失败。支持 p(N) / avg / med / rate / count;可设 abortOnFail: true 未达标即终止;可用 {tag:值} 对特定 URL 分别设阈。用于"性能达不达标"。
Logs(日志) — console.log / info / error / debug / warn 输出调试信息;k6 run test.js --console-output=test.log 可写入文件。
2、指标
1)k6 中每一个指标,都归属以下 4 种类型中的某一种。理解类型,就看懂了指标"能算出什么统计值"。
-
想看"总数"→ Counter;想看"当前值"→ Gauge;
-
想看"百分比"→ Rate;想看"分布/分位"→ Trend。
| 名称 | 描述 |
| Counter | 计数器,对值进行累加 |
| Gauge | 最小值、最大值和最后一个值。 |
| Rate | 百分比 |
| Trend | 最小值、最大值、平均值和百分位数的统计数据指标 |
2)K6 始终都会收集的指标
|
指标名 |
类型 |
含义 |
|---|---|---|
|
|
Gauge |
当前活跃的虚拟用户数 |
|
|
Gauge |
测试期间允许的最大 VU 数(配置上限) |
|
|
Counter |
VU 函数(default 函数)被执行的累计总次数 |
|
|
Trend |
完成一次完整迭代所花的时间 |
|
|
Counter |
因 VU 不足或达到迭代超时,未能启动的迭代数 |
|
|
Counter |
VU 接收到的数据总量(字节) |
|
|
Counter |
VU 发送的数据总量(字节) |
|
|
Rate |
断言(check)的通过率 |
3)HTTP 特有的指标
| 名称 | 类型 | 描述 |
| http_reqs | Counter | 总请求数量 |
| http_req_blocked | Trend | 在发起请求之前被阻塞的时间 |
| http_req_connecting | Trend | 建立到远程主机的TCP连接所花费的时间。 |
| http_req_tls_handshaking | Trend | 与远程主机握手建立TLS会话所花费的时间 |
| http_req_sending | Trend | 将数据发送到远程主机所花费的时间 |
| http_req_waiting | Trend | 等待远程主机响应所花费的时间 |
| http_req_receiving | Trend | 从远程主机接收响应数据所花费的时间 |
| http_req_duration | Trend | 请求的总时间。它等于http_req_blocked+http_req_connecting+http_req_tls_handshaking+http_req_sending + http_req_waiting + http_req_receiving |
| http_req_failed | Rate | 失败请求率 |
每一个 http 都会返回一个 HTTP Response 对象,下面是常用的一些属性。
| 属性 | 类型 |
| Response.body | HTTP 响应正文 |
| Response.cookies | 响应 cookies ,属性是 cookie 名称,值是 cookie 对象数组 |
| Response.error | 发送请求失败后的错误信息。 |
| Response.error_code | 错误码 |
| Response.headers | 标头,键值对 |
| Response.status | 从服务器收到的 HTTP 响应代码 |
| Response.timings | 耗时(以毫秒为单位) |
| Response.timings.blocked | = http_req_blocked |
| Response.timings.connecting | = http_req_connecting |
| Response.timings.tls_handshaking | = http_req_tls_handshaking |
| Response.timings.sending | = http_req_sending |
| Response.timings.waiting | = http_req_waiting |
| Response.timings.receiving | = http_req_receiving |
| Response.timings.duration | = http_req_duration |
4)自定义自己的指标
当内置指标不够时,可用 k6/metrics 模块自建,类型任选上面 4 种之一:
import http from 'k6/http';
import { Trend } from 'k6/metrics';
export let options = {
vus: 100,
duration: '10s',
};
// 新建一个类型为 Trend 名为 sending_time 的自定制指标
let sendingTime = new Trend('sending_time');
export default function () {
let res = http.get('http://www.baidu.com');
sendingTime.add(res.timings.sending);
}

3、Option配置
Vus:指定要同时运行的虚拟用户数量,必须是一个整数,和 duration 搭配使用。默认值:1
export let options = {
vus: 10,
duration: '10s',
};
k6 run -u 10 test.js
k6 run --vus 10 test.js
Duration:一个字符串,指定测试运行的总持续时间,与 vus 选项一起使用。默认值:null
export let options = {
vus: 10,
duration: '10s',
};
k6 run -u 10 --d 20s test.js
k6 run --vus 10 --duration 20s test.js
User Agent:发送 HTTP 请求时指定 User-Agent 标头。默认值:k6/0.27.0 (https://k6.io/) 取决于你 k6 的版本
export let options = {
userAgent: 'Mozilla/5.0',
};
k6 run --user-agent 'Mozilla/5.0' test.js
TLS Version:表示允许在与服务器交互中使用的唯一 SSL/TLS 版本的字符串,或者一个指定允许使用的“最小”和“最大”版本的对象。 默认值:null (允许所有版本)
export let options = {
tlsVersion: 'tls1.2',
};
export let options = {
tlsVersion: {
min: 'ssl3.0',
max: 'tls1.2',
},
};
TLS Cipher Suites:允许在与服务器的 SSL/TLS 交互中使用的密码套件列表。由于底层 go 实现的限制,不支持更改 TLS 1.3 的密码,并且不会执行任何操作。 默认值:null(允许所有)
export let options = {
tlsCipherSuites: [
'TLS_RSA_WITH_RC4_128_SHA',
'TLS_RSA_WITH_AES_128_GCM_SHA256',
],
};
TLS Auth: tls 身份验证。默认值:null
export let options = {
tlsAuth: [
{
domains: ['example.com'],
cert: open('mycert.pem'),
key: open('mycert-key.pem'),
},
],
};
Throw:一个布尔值,true or false ,指定是否在失败的 HTTP 请求上抛出异常。 默认值:false
export let options = {
throw: true,
};
k6 run --throw test.js
k6 run -w test.js
Thresholds:一组阈值规范,用于根据指标数据配置在何种条件下测试成功与否,测试通过或失败。默认值:null
export let options = {
thresholds: {
http_req_duration: ['avg<100', 'p(95)<200'],
'http_req_connecting{cdnAsset:true}': ['p(95)<100'],
},
};
Tags:指定应在所有指标中设置为测试范围的标签。如果在请求、检查或自定义指标上指定了同名标签,它将优先于测试范围的标签。 默认值:null
export let options = {
tags: {
name: 'value',
},
};
k6 run --tag NAME=VALUE test.js
RPS:每秒发出的最大请求数。 默认值:0
export let options = {
rps: 500,
};
k6 run --rps 500 test.js
Paused:是否可以暂停和和恢复的方式运行脚本,暂停启动后需要使用另外的窗口执行k6 resume 恢复使用。在恢复窗口可以实时的查看脚本的运行情况。 启动后不支持暂停, 默认值:false
export let options = {
paused: true,
};
k6 run --paused test.js
k6 run --p test.js
No VU Connection Reuse:布尔值,是否复用 TCP 链接,各 VU 不共享连接池。默认值:false
export let options = {
noVUConnectionReuse: true,
};
k6 run --no-vu-connection-reuse test.js
No Usage Report:布尔值,是否给 K6 发送使用报告,true 值不会发使用报告。 默认值:false
k6 run --no-usage-report test.js
No Thresholds:布尔值,是否禁用阈值。默认是:false
k6 run --no-thresholds test.js
No Summary:是否禁用测试结束生成的概要。默认值:false
k6 run --no-summary test.js
No Cookies Reset:是否重置 Cookies,false 每次迭代都会重置 Cookie ,true 会在迭代中持久化 Cookie 。默认值:false
export let options = {
noCookiesReset: true,
};
No Connection Reuse:是否禁用保持活动连接,全局禁用 keep-alive,默认值:false
export let options = {
noConnectionReuse: true,
};
k6 run --no-connection-reuse test.js
Minimum Iteration Duration:指定默认函数每次执行的最短持续时间,任何小于此值的迭代都将剩余时间内休眠,直到达到指定的最小持续时间。默认值:0
export let options = {
minIterationDuration: '10s',
};
k6 run --min-iteration-duration '1s' test.js
Max Redirects:最大重定向,默认值:10
export let options = {
maxRedirects: 10,
};
k6 run --max-redirects 10 test.js
Batch: batch 同时调用的最大连接总数,如果同时有 20 api 请求需要发出 ,batch 值是 15,那么将会立即发出 15 个请求,其余的请求会进行一个排队。默认值:20
export let options = {
batch: 15,
};
k6 run --batch 10 test.js
Batch per host:batch 对同一个主机名同时进行的最大并行连接数。默认值:6
export let options = {
batchPerHost: 5,
};
k6 run --batch-per-host 10 test.js
Blacklist IPs:黑名单。默认值:null
export let options = {
blacklistIPs: ['10.0.0.0/8'],
};
k6 run --blacklist-ip= 10.0.0.0/8 test.js
Block Hostnames:基于模式匹配字符串来阻止主机,如 *.example.com , 默认值:null
export let options = {
blockHostnames: ["test.k6.io" , "*.example.com"],
};
k6 run --block-hostnames="test.k6.io,*.example.com" test.js
Discard Response Bodies:是否应丢弃响应正文,将 responseType 的默认值修改成 none,建议设置成 true,可以减少内存暂用和GC使用,有效的减少测试机的负载。默认值:false
export let options = {
discardResponseBodies: true,
};
HTTP Debug:记录所有HTTP请求和响应。默认情况下排除正文,包括正文使用 --http-debug=full 默认值:false
export let options = {
httpDebug: 'full',
};
k6 run --http-debug test.js
4、Checks 检查
Checks 类似断言,不同在于 Checks 不会停止当前的脚本。指标都可以作为检查的项目。
import http from 'k6/http';
import { sleep } from 'k6';
import { check } from 'k6';
export let options = {
vus: 100,
duration: '10s',
};
export default function () {
let res = http.get('http://test.k6.io/');
check(res, {
'状态码为200': (r) => r.status === 200,
'响应时间小于200ms': (r) => r.timings.duration < 200,
'等待远程主机响应时间小于200ms': (r) => r.timings.waiting < 200,
});
}

5、Thresholds 阈值
阈值是用来指定被测系统的性能预期的通过/失败标准。阈值用来分析性能指标并确定最终测试结果。内置的指标都可以作为阈值。
K6 中包含的四种度量类型每一种都提供了自己的一组可用于阈值表达式的聚合方法。
- Counter: count and rate
- Gauge:value
- Rate:rate
- Trend:p(N)
import http from 'k6/http';
import { Trend, Rate, Counter, Gauge } from 'k6/metrics';
export let GaugeContentSize = new Gauge('ContentSize');
export let TrendRTT = new Trend('RTT');
export let options = {
vus: 10,
duration: '10s',
thresholds: {
// 发出的请求数量需要大于1000
http_reqs:['count>1000'],
// 错误率应该小于0.01%
http_req_failed: ['rate<0.01'],
// 返回的内容必须小于 4000 字节。
ContentSize: ['value<4000'],
// p(N) 其中 N 是一个介于 0.0 和 100.0 之间的数字,表示要查看的百分位值,例如p(99.99) 表示第 99.99 个百分位数。这些值的单位是毫秒。
// 90% 的请求必须在 400 毫秒内完成,95% 必须在 800 毫秒内完成,99.9% 必须在 2 秒内完成
http_req_duration: ['p(90) < 400', 'p(95) < 800', 'p(99.9) < 2000'],
// 99% 响应时间必须低于 300 毫秒,70% 响应时间必须低于 250 毫秒,
// 平均响应时间必须低于 200 毫秒,中位响应时间必须低于 150 毫秒,最小响应时间必须低于 100 毫秒
RTT: ['p(99)<300', 'p(70)<250', 'avg<200', 'med<150', 'min<100'],
},
};
export default function () {
let res = http.get('http://www.baidu.com');
TrendRTT.add(res.timings.duration);
GaugeContentSize.add(res.body.length);
}

阈值标签,测试中可以给指定的 url 或者特定标签上使用阈值。
import http from 'k6/http';
import { sleep } from 'k6';
import { Rate } from 'k6/metrics';
export let options = {
vus: 10,
duration: '10s',
thresholds: {
// type 为 baidu 使用
'http_req_duration{type:baidu}': ['p(95)<500'],
// type 为 bing 使用
'http_req_duration{type:bing}': ['p(95)<200'],
},
};
export default function () {
let res1 = http.get('https://www.baidu.com', {
tags: { type: 'baidu' },
});
let res2 = http.get('https://cn.bing.com/', {
tags: { type: 'bing' },
});
let res3 = http.batch([
[
'GET',
' https://www.baidu.com',
null,
{ tags: { type: 'baidu' } },
],
[
'GET',
'https://cn.bing.com/',
null,
{ tags: { type: 'bing' } },
],
]);
}

默认情况下没有达标阈值标准是不会停止脚本的,通过设置阈值的 abortOnFail: true 来终止。
import http from 'k6/http';
export let options = {
vus: 10,
duration: '10s',
thresholds: {
http_req_duration: [{threshold: 'p(99) < 10', abortOnFail: true}],
},
};
export default function () {
let res = http.get('http://www.baidu.com');
}
对通过的阈值前面会有一个✓,而失败的则会有一个 ✗ 。只有满足所有阈值的情况下测试才算通过。

6、日志输出
输出到控制台。
import http from 'k6/http';
export let options = {
vus: 10,
duration: '2s',
};
export default function () {
let res = http.get('http://www.baidu.com');
console.log('log')
console.info('info');
console.error('err');
console.debug('debug')
console.warn('warn')
}
输出到文件,输出到文件的同时控制台不在输出。
k6 run test.js --console-output=test.log
四、InfluxDB + Grafana 可视化测试结果
Docker 启动 InfluxDB
docker pull tutum/influxdb
# 8083是influxdb的web管理工具端口,8086是influxdb的HTTP API端口
docker run -d -p 8083:8083 -p8086:8086 --expose 8090 --expose 8099 --name influxsrv tutum/influxdb
Docker 启动 Grafana,
docker pull grafana/grafana
docker run -d -p 3000:3000 grafana/grafana
新建一个 K6test 数据库,访问 "http://xxxxx:8083" InfluxDB web 管理页面,新建一个 K6test 数据库

配置 Grafana 数据源

选择 InfluxDB

填写域名端口和数据库,点击 sava&test 。出现 Data source is working 表示成功,如遇到问题查看一下端口是否放行。

导入仪表盘

通过 ID 导入,输入 2587 点击 load 数据源选择 InfluxDB 点击 Import

官方还有几款仪表盘
将 K6 的测试指标导入到 InfluxDB
k6 run --out influxdb=http://xxxxx:8086/K6test test.js
效果图


浙公网安备 33010602011771号