3. k6 核心 API:http.get、http.post、check、group、sleep 函数详解与使用场景
在 k6 脚本开发中,http.get、http.post、check、group 和 sleep 是构建压测场景的五个基石。理解它们的参数、返回值与最佳实践,是编写可靠容量压测脚本的前提。本章将逐一拆解每个 API 的签名、行为与典型用法,并通过对比表格和流程图帮助你建立系统认知。
3.1 核心 API 概览
下表从函数签名、主要参数、返回值与典型用途四个维度,对这五个 API 进行横向对比:
| API | 函数签名 | 核心参数 | 返回值 | 典型用途 |
|---|---|---|---|---|
| http.get | http.get(url, [params]) |
url, params (headers, tags, timeout 等) | HttpResponse 对象 |
查询类接口压测,如 GET 商品列表 |
| http.post | http.post(url, [body], [params]) |
url, body (string/JSON/ArrayBuffer), params | HttpResponse 对象 |
写入类接口压测,如提交订单 |
| check | check(val, checks, [tags]) |
val (待检查值), checks (对象键值对), tags | boolean (全部通过为 true) |
断言响应状态码、响应体字段、响应时间 |
| group | group(name, fn) |
name (分组名称), fn (回调函数) | fn 的返回值 | 组织业务事务,聚合报告中的耗时与通过率 |
| sleep | sleep(t) |
t (秒,支持小数) | 无 | 模拟用户思考时间,控制请求间隔 |
3.2 http.get 与 http.post:请求执行器
3.2.1 基本用法
http.get 和 http.post 都返回 HttpResponse 对象,该对象包含 status、body、headers、timings 等关键属性。以下是一个同时使用 GET 和 POST 的示例:
import http from 'k6/http';
import { check } from 'k6';
export default function () {
// GET 请求:查询用户列表
let getRes = http.get('https://api.example.com/users', {
headers: { 'Accept': 'application/json' },
tags: { name: 'getUsers' },
});
// POST 请求:创建新用户
let payload = JSON.stringify({
name: 'test_user',
email: 'test@example.com',
});
let postRes = http.post('https://api.example.com/users', payload, {
headers: { 'Content-Type': 'application/json' },
tags: { name: 'createUser' },
});
// 对两个响应分别做检查
check(getRes, {
'GET status is 200': (r) => r.status === 200,
});
check(postRes, {
'POST status is 201': (r) => r.status === 201,
'POST has user id': (r) => JSON.parse(r.body).id !== undefined,
});
}
3.2.2 参数详解
第二个参数 params 支持以下常用选项:
| 参数名 | 类型 | 说明 | 示例 |
|---|---|---|---|
| headers | 对象 | 自定义请求头 | { 'Authorization': 'Bearer ' } |
| tags | 对象 | 自定义标签,用于报告聚合与过滤 | { name: 'login', type: 'auth' } |
| timeout | 字符串 | 请求超时时间,默认 60s | '30s' |
| cookies | 对象 | 设置请求携带的 Cookie | { 'session_id': 'abc123' } |
| auth | 字符串 | HTTP Basic Auth,格式 "username:password" |
'admin:secret' |
__ENV 环境变量或 SharedArray 加载机密数据。另外,POST 请求的 body 如果是 JSON,务必先 JSON.stringify,否则 k6 会按字符串原样发送。
3.3 check:断言与阈值检查
3.3.1 语法与行为
check 是 k6 中最常用的断言工具。它接受一个待检查值(通常是 HttpResponse),以及一个或多个检查条件。每个条件是一个函数,返回 true 或 false。k6 会在报告中统计每个检查的通过/失败次数。
check(res, {
'status is 2xx': (r) => r.status >= 200 && r.status < 300,
'response time < 500ms': (r) => r.timings.duration < 500,
'body contains success': (r) => r.body.includes('success'),
});
3.3.2 在容量压测中的水位口径作用
在容量压测中,check 不仅仅是“通过/失败”的标记,更是水位口径边界的量化工具。你可以通过 check 定义“可接受响应时间”和“错误率阈值”,从而在报告中快速识别系统是否达到容量瓶颈。
| 检查类型 | 检查表达式 | 水位口径含义 |
|---|---|---|
| 状态码检查 | r.status === 200 |
接口可用性边界 |
| 响应时间检查 | r.timings.duration < 1000 |
响应时间水位线(1s) |
| 业务字段检查 | JSON.parse(r.body).code === 0 |
业务成功边界 |
| 响应体大小检查 | r.body.length < 10240 |
数据量水位线(10KB) |
3.4 group:事务组织与报告聚合
3.4.1 为什么需要 group
当压测脚本包含多个步骤(如登录→搜索→下单→支付)时,直接平铺所有请求会导致报告难以阅读。group 可以将一组请求包裹为一个逻辑事务,k6 会自动计算该组的总耗时、通过率和请求分布。
import { group } from 'k6';
export default function () {
group('用户登录流程', function () {
let loginRes = http.post('https://api.example.com/login', {
username: 'user1',
password: 'pass1',
});
check(loginRes, { 'login success': (r) => r.status === 200 });
});
group('商品搜索流程', function () {
let searchRes = http.get('https://api.example.com/search?q=phone');
check(searchRes, { 'search ok': (r) => r.status === 200 });
});
}
3.4.2 group 的嵌套与阈值
group 支持嵌套,但建议不超过 3 层,以免报告过于复杂。你还可以在 options.thresholds 中针对 group 名称设置阈值:
export let options = {
thresholds: {
'group_duration{group:::用户登录流程}': ['p(95)<2000'],
'group_duration{group:::商品搜索流程}': ['p(95)<1000'],
},
};
3.5 sleep:模拟用户思考时间
3.5.1 基本用法
sleep 函数暂停当前 VU 的执行,单位是秒,支持小数。它用于模拟真实用户在操作之间的停顿,避免压测变成“无脑洪水”式请求。
import { sleep } from 'k6';
export default function () {
http.get('https://api.example.com/home');
sleep(1); // 暂停 1 秒
http.get('https://api.example.com/search?q=test');
sleep(0.5); // 暂停 0.5 秒
http.post('https://api.example.com/order');
sleep(2.5); // 暂停 2.5 秒
}
3.5.2 在容量压测中的水位口径意义
sleep 时间直接影响请求速率(RPS)和并发用户数(VU)之间的关系。在固定 VU 数下,sleep 越短,RPS 越高,系统压力越大。因此,sleep 是调节“压测水位”的精细旋钮。
| 场景 | sleep 设置 | 模拟行为 | 对水位的影响 |
|---|---|---|---|
| 轻量浏览 | 3~5 秒 | 用户慢速浏览 | 低水位,RPS 较低 |
| 中等操作 | 1~2 秒 | 用户正常操作 | 中水位,接近真实负载 |
| 高并发压测 | 0~0.5 秒 | 用户快速连续操作 | 高水位,探测系统上限 |
| 边界测试 | 0 秒 | 无思考时间,极限压力 | 最高水位,测试系统容量 |
3.6 五函数协作流程图
下面的 SVG 图展示了在一个典型压测迭代中,五个 API 的调用顺序与协作关系:
3.7 综合示例:容量压测脚本片段
以下示例将五个 API 组合成一个完整的容量压测迭代,并体现了“水位口径边界”的检查思路:
import http from 'k6/http';
import { check, group, sleep } from 'k6';
export default function () {
group('商品详情与下单链路', function () {
// 步骤1:查看商品详情(GET)
let detailRes = http.get('https://api.example.com/product/1001', {
tags: { step: 'detail' },
});
check(detailRes, {
'商品详情状态码 200': (r) => r.status === 200,
'商品详情响应时间 < 800ms': (r) => r.timings.duration < 800,
'商品详情包含价格': (r) => r.body.includes('"price"'),
});
sleep(1);
// 步骤2:提交订单(POST)
let orderPayload = JSON.stringify({
productId: 1001,
quantity: 1,
});
let orderRes = http.post('https://api.example.com/order', orderPayload, {
headers: { 'Content-Type': 'application/json' },
tags: { step: 'order' },
});
check(orderRes, {
'下单状态码 201': (r) => r.status === 201,
'下单响应时间 < 1500ms': (r) => r.timings.duration < 1500,
'下单返回订单号': (r) => JSON.parse(r.body).orderId !== undefined,
});
sleep(2);
});
}
3.8 常见误区与避坑指南
- 误区一:在 check 中直接使用
===比较字符串,忽略 JSON 解析。应使用JSON.parse(r.body)后再比较业务字段。 - 误区二:group 内部忘记使用
return或回调函数未正确闭合,导致 group 统计异常。确保 group 的回调函数完整执行所有请求。 - 误区三:sleep 时间固定不变,导致压测行为过于规律。建议使用
random函数生成随机 sleep 时间,更贴近真实用户行为。 - 误区四:http.get 和 http.post 的 tags 参数未设置,导致报告中无法按接口维度过滤。始终为每个请求设置有意义的 tags。
thresholds 联动。例如设置 checks['my_check']: ['rate<0.05'],当 check 失败率超过 5% 时,k6 自动标记测试为失败,从而实现“水位口径边界”的自动化判定。