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 (秒,支持小数) 无 模拟用户思考时间,控制请求间隔
关键认知:http.get 与 http.post 是“执行者”,check 是“裁判”,group 是“组织者”,sleep 是“节拍器”。在容量压测中,这五个函数共同决定了脚本的逼真度与可观测性。

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'
避坑提示:不要在 params 中直接写死敏感凭据。使用 k6 的 __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 个 check:状态码、响应时间、业务成功标志。这三大检查构成了“容量水位口径”的三角验证体系。

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'],
  },
};
使用场景:在容量压测中,group 最适合用来包裹“完整业务链路”。例如“浏览商品→加入购物车→提交订单→支付”,每个链路作为一个 group,便于对比不同链路的耗时瓶颈。

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 秒 无思考时间,极限压力 最高水位,测试系统容量
避坑提示:不要在容量压测的“预热阶段”使用 0 秒 sleep,这可能导致初始瞬间压力过大,触发限流或熔断。建议预热阶段使用 1~2 秒 sleep,稳定后再逐步降低。

3.6 五函数协作流程图

下面的 SVG 图展示了在一个典型压测迭代中,五个 API 的调用顺序与协作关系:

k6 核心 API 协作流程图(单次迭代) 开始迭代 group("业务事务") http.get / http.post 发送请求 check 断言验证 sleep 思考时间 结束迭代 说明:group 包裹业务事务,内部包含 http 请求、check 断言和 sleep 思考时间,形成完整的压测迭代单元

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);
  });
}
水位口径边界要点:上述脚本中,每个 check 都定义了一个明确的“水位线”——响应时间 800ms 和 1500ms 就是两个口径边界。当压测进行时,如果 check 失败率超过阈值(如 5%),即表明系统已达到或超过该水位线的容量上限。

3.8 常见误区与避坑指南

  • 误区一:在 check 中直接使用 === 比较字符串,忽略 JSON 解析。应使用 JSON.parse(r.body) 后再比较业务字段。
  • 误区二:group 内部忘记使用 return 或回调函数未正确闭合,导致 group 统计异常。确保 group 的回调函数完整执行所有请求。
  • 误区三:sleep 时间固定不变,导致压测行为过于规律。建议使用 random 函数生成随机 sleep 时间,更贴近真实用户行为。
  • 误区四:http.get 和 http.post 的 tags 参数未设置,导致报告中无法按接口维度过滤。始终为每个请求设置有意义的 tags。
进阶建议:在容量压测脚本中,将 check 的失败率与 thresholds 联动。例如设置 checks['my_check']: ['rate<0.05'],当 check 失败率超过 5% 时,k6 自动标记测试为失败,从而实现“水位口径边界”的自动化判定。