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 请求与响应流程
下图展示了从参数构造到响应解析的完整数据流,帮助理解各环节的协作关系:
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 数 | 增加数据量或使用随机索引 |