4. HTTP 请求与参数化:核心方法与响应解析

在 k6 容量压测中,HTTP 请求是脚本与目标系统交互的唯一通道。本章将系统讲解 http.get() 与 http.post() 的完整参数结构,以及如何通过参数化实现动态请求负载。掌握这些内容后,你将能够构造任意复杂度的 HTTP 请求,并准确解析响应数据用于断言与巡检。

4.1 HTTP 请求方法速览

k6 内置的 http 模块提供了所有标准 HTTP 方法。在容量压测巡检场景中,最常用的是 GET 和 POST,但 PUT、PATCH、DELETE 等方法的参数结构完全一致。

方法 典型用途 参数结构
http.get(url, [params]) 查询资源、健康检查、状态巡检 url + 可选 params 对象
http.post(url, [body], [params]) 提交数据、创建资源、登录认证 url + 可选 body + 可选 params
http.put(url, [body], [params]) 全量更新资源 同 post
http.patch(url, [body], [params]) 部分更新资源 同 post
http.del(url, [params]) 删除资源 同 get
实战提示: 在巡检脚本中,GET 请求通常用于探测接口可用性,POST 请求用于模拟用户提交操作。建议将请求方法封装为函数,便于复用和参数化。

4.2 参数对象(params)详解

params 是 k6 HTTP 请求的核心配置容器,支持 headers、tags、cookies、auth 等 10+ 属性。以下是最常用的配置项:

属性 类型 说明 示例
headers 对象 自定义请求头 {'Content-Type': 'application/json'}
tags 对象 自定义标签,用于结果分类 {name: 'login-api', env: 'prod'}
cookies 对象 设置请求 Cookie {session_id: 'abc123'}
auth 字符串 HTTP Basic 认证 'username:password'
timeout 字符串 请求超时时间 '10s'
params 对象 URL 查询参数 {page: '1', limit: '20'}

4.3 响应对象结构

每个 HTTP 请求都会返回一个 http.Response 对象,包含完整的响应信息。理解其结构是编写断言和提取数据的基础。

// 响应对象核心属性
let res = http.get('https://api.example.com/users');

// 状态码
console.log(res.status);           // 200

// 响应体(字符串)
console.log(res.body);             // '{"users":[...]}'

// 响应头
console.log(res.headers['Content-Type']);  // 'application/json'

// 响应时间(毫秒)
console.log(res.timings.duration); // 123.45

// JSON 解析
let jsonData = res.json();
console.log(jsonData.users[0].name);

// 检查响应是否成功
check(res, {
  'status is 200': (r) => r.status === 200,
  'response time < 500ms': (r) => r.timings.duration < 500
});
关键字段速查:
  • res.status — HTTP 状态码(数字)
  • res.body — 响应体原始字符串
  • res.json() — 将 JSON 响应解析为对象
  • res.headers — 响应头对象(键小写)
  • res.timings.duration — 总请求耗时(毫秒)
  • res.error_code — 网络错误码(如 1101 超时)

4.4 参数化实战:动态请求构造

容量压测的核心在于参数化——每次迭代使用不同的数据,避免缓存命中导致测试失真。以下展示三种常用参数化模式:

4.4.1 从数组轮询参数

import http from 'k6/http';
import { check } from 'k6';

// 参数化数据源
const userIds = ['u001', 'u002', 'u003', 'u004', 'u005'];
const tokens = ['tok_a', 'tok_b', 'tok_c', 'tok_d', 'tok_e'];

export default function () {
  // 使用 __VU 和 __ITER 组合实现轮询
  let idx = (__VU + __ITER) % userIds.length;
  
  let params = {
    headers: {
      'Authorization': `Bearer ${tokens[idx]}`,
      'Content-Type': 'application/json'
    },
    tags: { user: userIds[idx] }
  };

  let res = http.get(`https://api.example.com/users/${userIds[idx]}`, params);

  check(res, {
    'user query success': (r) => r.status === 200,
    'response time < 300ms': (r) => r.timings.duration < 300
  });
}

4.4.2 POST 请求带 JSON Body

import http from 'k6/http';
import { check } from 'k6';

const payloads = [
  { username: 'alice', role: 'admin' },
  { username: 'bob', role: 'editor' },
  { username: 'charlie', role: 'viewer' }
];

export default function () {
  let idx = __ITER % payloads.length;
  let body = JSON.stringify(payloads[idx]);

  let params = {
    headers: { 'Content-Type': 'application/json' },
    tags: { payload_idx: String(idx) }
  };

  let res = http.post('https://api.example.com/users', body, params);

  check(res, {
    'user created': (r) => r.status === 201,
    'has user id': (r) => r.json().id !== undefined
  });
}

4.4.3 动态查询参数

import http from 'k6/http';

const searchTerms = ['k6', 'load testing', 'performance'];

export default function () {
  let q = searchTerms[__ITER % searchTerms.length];
  
  let res = http.get('https://api.example.com/search', {
    params: { q: q, page: '1', limit: '20' },
    headers: { 'Accept': 'application/json' }
  });

  console.log(`Search for "${q}" returned ${res.json().total} results`);
}
避坑指南:
  • 不要在 default 函数外部调用 http.get/post,k6 只会在 VU 迭代中执行请求。
  • JSON body 必须使用 JSON.stringify() 序列化,不能直接传对象。
  • 参数化数据量应大于并发 VU 数,避免所有 VU 使用同一份数据。

4.5 HTTP 请求与响应流程

下图展示了从参数构造到响应解析的完整数据流,帮助理解各环节的协作关系:

HTTP 请求与响应处理流程 参数化数据源 构造 params 对象 http.get/post headers/tags/body 响应对象 响应解析 res.status res.json() res.timings check 断言 💡 巡检最佳实践: 每次请求后立即执行 check 断言,结合 tags 标记异常请求,便于在报告中快速定位问题。

4.6 完整实战示例:用户登录巡检

以下脚本综合运用了 POST 请求、参数化、响应解析和断言,模拟多用户登录巡检场景:

import http from 'k6/http';
import { check, sleep } from 'k6';

// 参数化用户数据
const users = [
  { email: 'alice@test.com', password: 'pass123' },
  { email: 'bob@test.com', password: 'pass456' },
  { email: 'charlie@test.com', password: 'pass789' }
];

export default function () {
  // 轮询用户
  let user = users[__ITER % users.length];

  // 构造请求
  let payload = JSON.stringify({
    email: user.email,
    password: user.password
  });

  let params = {
    headers: { 'Content-Type': 'application/json' },
    tags: { user_email: user.email }
  };

  // 发送登录请求
  let res = http.post('https://api.example.com/login', payload, params);

  // 响应断言
  check(res, {
    'login success': (r) => r.status === 200,
    'has ': (r) => r.json(). !== undefined,
    'response fast': (r) => r.timings.duration < 1000
  });

  // 提取  用于后续请求(可选)
  if (res.status === 200) {
    let  = res.json().;
    // 后续请求可使用此 
  }

  sleep(1);
}
巡检脚本检查清单:
  • ✅ 每个请求都包含 tags 标记,便于问题定位
  • ✅ 使用 check() 验证状态码、响应时间和关键字段
  • ✅ 参数化数据覆盖不同用户场景
  • ✅ 响应时间阈值根据业务 SLA 设定(如 < 1000ms)
  • ✅ 错误请求通过 res.error_code 区分网络层问题

4.7 常见错误与排查

错误现象 可能原因 解决方案
请求返回 400 Body 格式错误或缺少必填字段 检查 JSON.stringify 是否正确,对比 API 文档
请求返回 401 认证头缺失或 过期 检查 headers 中的 Authorization 字段
请求超时 目标服务响应慢或网络延迟 增加 timeout 参数,检查网络连通性
响应解析失败 响应体不是合法 JSON 先打印 res.body 查看原始内容,使用 try-catch
参数化数据重复 数据源长度小于 VU 数 增加数据量或使用随机索引