k6
- grafana/k6
- AGPL-3.0, Golang
- 现代化压测工具
- 使用 JS 编写压测逻辑
- 支持非常丰富的插件: JS 扩展、输出扩展
- JS 引擎 dop251/goja
brew install k6
# https://github.com/grafana/k6/releases
curl -LO https://github.com/grafana/k6/releases/download/v0.45.0/k6-v0.45.0-linux-amd64.tar.gz
tar zxvf k6-v0.45.0-linux-amd64.tar.gz --strip-components 1
./k6
k6 run script.js
import { check, sleep } from 'k6';
import http from 'k6/http';
export const options = {
// 单个
vus: 10,
duration: '30s',
// or 多个场景
// stages: [
// { duration: '30s', target: 20 },
// { duration: '1m30s', target: 10 },
// { duration: '20s', target: 0 },
// ],
// 目标,SLO
thresholds: {
// Assert that 99% of requests finish within 3000ms.
http_req_duration: ['p(99) < 3000'],
},
};
export default function () {
const res = http.get('https://wener.me');
check(res, { 'status was 200': (r) => r.status == 200 });
sleep(1);
}
- virtual users (VUs)
__ENV
// 1. init code - 每个 UV 调用 1 次
export function setup() {
// 2. setup code - 全局只会调用 1 次
return {};
}
// data 为 setup 返回结果
export default function (data) {
// 3. VU code - 每次迭代调用 1 次
}
// data 为 setup 返回结果
export function teardown(data) {
// 4. teardown code - 全局调用 1 次,如果 setup 失败则不调用
}
// 通过配置 scenarios.*.exec 来指定执行
export function webtest() {}
- handleSummary
k6 run flags | for |
---|---|
-u,--vus INT | 默认 1, 虚拟用户数 |
-d,--duration DURATION | 持续时间 |
-i,--iterations INT | 迭代次数 |
-s,--stage STAGE | [duration]:[target] |
--rps INT | 限制每秒请求数 |
--http-debug | 默认 headers, dump http request |
--http-debug=full | 包含 body |
context
- instance
- scenario
- vu
import exec from 'k6/execution';
export default function () {
console.log(`Execution context
Instance info
-------------
Vus active: ${exec.instance.vusActive}
Iterations completed: ${exec.instance.iterationsCompleted}
Iterations interrupted: ${exec.instance.iterationsInterrupted}
Iterations completed: ${exec.instance.iterationsCompleted}
Iterations active: ${exec.instance.vusActive}
Initialized vus: ${exec.instance.vusInitialized}
Time passed from start of run(ms): ${exec.instance.currentTestRunDuration}
Scenario info
-------------
Name of the running scenario: ${exec.scenario.name}
Executor type: ${exec.scenario.executor}
Scenario start timestamp: ${exec.scenario.startTime}
Percenatage complete: ${exec.scenario.progress}
Iteration in instance: ${exec.scenario.iterationInInstance}
Iteration in test: ${exec.scenario.iterationInTest}
Test info
---------
All test options: ${exec.test.options}
VU info
-------
Iteration id: ${exec.vu.iterationInInstance}
Iteration in scenario: ${exec.vu.iterationInScenario}
VU ID in instance: ${exec.vu.idInInstance}
VU ID in test: ${exec.vu.idInTest}
VU tags: ${exec.vu.tags}`);
}
xk6
- k6 Extensions
- Javascript
- Output
- https://k6.io/docs/extensions/get-started/explore/
- 参考
- szkiba/xk6-dashboard
- github.com/szkiba/xk6-dashboard@latest
- http://127.0.0.1:5665
--out dashboard=open
打开浏览器- 其他参数 host,port=5665,period=20s,config=.dashboard.js
- szkiba/xk6-top
- --out top
- szkiba/xk6-dotenv
- github.com/szkiba/xk6-dotenv@latest
.env.{development,test,production}.local
- $K6_ENV.env.{local,development,test,production}
.env
- grafana/xk6-output-timescaledb
- K6_TIMESCALEDB_PUSH_INTERVAL=1s
- grafana/xk6-sql
- 压测 SQL
- PostgreSQL, MySQL, MS SQL, SQLite3
- SQLite 需要
CGO_ENABLED=1
- avitalique/xk6-file
- 文件读写
- github.com/avitalique/xk6-file@latest
- szkiba/xk6-dashboard
go install go.k6.io/xk6/cmd/xk6@latest
xk6 build --with github.com/grafana/xk6-output-timescaledb --with github.com/szkiba/xk6-dotenv@latest
./k6
# -o $K6_OUT
K6_OUT=timescaledb=postgresql://k6:k6@timescaledb:5432/k6 k6 run script.js
timescaledb
- samples
- thresholds
- K6_TIMESCALEDB_PUSH_INTERVAL=1s
- 2min 约 500 万 条记录、2G
- 越长使用越多内存
k6 run --tag testid=SVR-$(date +%Y%m%d-%H%M)
CREATE TABLE IF NOT EXISTS samples (
ts timestamptz NOT NULL DEFAULT current_timestamp,
metric varchar(128) NOT NULL,
tags jsonb,
value real
);
CREATE TABLE IF NOT EXISTS thresholds (
id serial,
ts timestamptz NOT NULL DEFAULT current_timestamp,
metric varchar(128) NOT NULL,
tags jsonb,
threshold varchar(128) NOT NULL,
abort_on_fail boolean DEFAULT FALSE,
delay_abort_eval varchar(128),
last_failed boolean DEFAULT FALSE
);
SELECT create_hypertable('samples', 'ts');
CREATE INDEX IF NOT EXISTS idx_samples_ts ON samples (ts DESC);
CREATE INDEX IF NOT EXISTS idx_thresholds_ts ON thresholds (ts DESC);
-- 可以考虑
create index samples_ts_tags_testid on samples using btree (ts, ((tags ->> 'testid'::text)));
- https://github.com/grafana/xk6-output-timescaledb/blob/main/output.go
- https://github.com/grafana/xk6-output-timescaledb/
SQL
Setup
sudo apk add go
sudo chown $USER mkdir -p /data
mkdir -p /data/bin
go env -w GOPROXY=https://goproxy.cn,direct
go install go.k6.io/xk6/cmd/xk6@latest
GOBIN=/data/bin go install go.k6.io/xk6/cmd/xk6@latest
export PATH="/data/bin:$PATH"
# CGO_ENABLED=1 for SQLite
# --output /data/bin
xk6 build \
--with github.com/grafana/xk6-output-timescaledb \
--with github.com/szkiba/xk6-dotenv \
--with github.com/szkiba/xk6-dashboard \
--with github.com/avitalique/xk6-file \
--with github.com/szkiba/xk6-top \
--with github.com/grafana/xk6-sql
mv k6 /data/bin/
mkdir -p /data/k6
cd /data/k6
Target RPS
export const options = {
scenarios: {
constant_request_rate: {
executor: 'constant-arrival-rate',
rate: 1,
timeUnit: '1s',
duration: '1m',
preAllocatedVUs: 20,
maxVUs: 100,
},
stageOne: {
executor: 'constant-arrival-rate',
duration: '5m',
rate: 1000000, // 1mil QPS for 5 min
timeUnit: '1s',
maxVUs: 12500, // Some number with headroom where I try to judge, given some hypothetical SUT latency, how I can reach 1mil QPS?
},
stageTwo: {
executor: 'constant-arrival-rate',
duration: '5m',
startTime: '5m',
rate: 10000000, // 10mil QPS for 5 min
timeUnit: '1s',
maxVUs: 125000, // Ditto
},
// ...
},
};
- rate+timeUnit
- preAllocatedVUs, maxVUs
- https://k6.io/blog/how-to-generate-a-constant-request-rate-with-the-new-scenarios-api/
- https://github.com/grafana/k6-operator/issues/120