性能测试方法
从一次大促事故学性能测试:用 k6 完成第一次负载测试,读懂 P95 与吞吐量,理清负载、压力、稳定性测试的目标与工具选择。
前置知识:知道 HTTP 接口的请求/响应长什么样;装好 Node.js 或任一运行时 (k6 是独立的单个二进制,装完即用)。不需要先学 JMeter——本文用更轻的 k6 做第一次动手,JMeter 与 LoadRunner 的定位在工具地图一节讲清。
读完本文你应当能够:区分负载/压力/稳定性/尖峰四类测试目标;给一个本地 服务跑第一次负载测试并读懂分位延迟;说清”并发用户数”为什么不是”在线 人数”。接口「响应对不对」的功能那一半见《接口测试方法》。
1. 场景:大促开始十分钟,下单接口雪崩
复盘一次典型事故:营销活动十点开始,流量是平时的五倍,十点十分下单 接口超时告警,用户看到的是转圈和报错,订单量归零。事后复盘发现两个 事实:
- 这个接口从来没被压测过。团队对”它能扛多少”一无所知,容量规划 靠拍脑袋。
- 单接口功能测试全绿。每个接口单发请求都正常——直到一千个人同时 发。性能问题不是功能 bug,是”数量上去之后才暴露”的另一类 bug。
性能测试的全部意义就一句话:在用户替你发现容量上限之前,先在受控 环境里自己找到它。
2. 先分清四类测试,别上来就压
“压测”是口语,专业上先分清目标,因为不同目标的加压方式完全不同:
| 类型 | 目标 | 加压方式 | 典型问题 |
|---|---|---|---|
| 负载测试 | 预期负载下表现是否达标 | 固定在预期并发 | 日常高峰顶得住吗 |
| 压力测试 | 找到系统的极限与崩溃形态 | 阶梯递增直到崩 | 上限多少,怎么崩的 |
| 稳定性测试 | 长时间运行是否衰减 | 中等负载跑数小时起 | 有没有内存泄漏 |
| 尖峰测试 | 突发流量冲击后的恢复 | 瞬时拉高再回落 | 大促瞬时流量扛得住吗 |
新手最常见错误:想做负载测试,实际跑了压力测试(一直加压到崩), 拿到一个”极限 TPS”却回答不了”日常高峰的响应时间达标吗”。
3. 关键指标:并发用户数不等于在线人数
| 指标 | 说明 |
|---|---|
| 响应时间 | 看 P95/P99 分位,不看平均值——平均 200ms 可能掩盖着 P99 3s 的长尾 |
| 吞吐量 | 单位时间处理的请求数(TPS/QPS) |
| 并发用户数 | 同一时刻正在发请求的用户数。不等于在线数:在线用户大部分时间在浏览,只有一小部分在发请求 |
| 错误率 | 失败请求占比 |
| 资源占用 | 服务端 CPU、内存——判断瓶颈在哪台机器 |
“并发 1000”到底怎么估?典型换算:在线 10 万人,其中同时发请求的占 1% 到 5%,即 1000 到 5000 并发。拍脑袋拍的是这个百分比,而它应该来自 对生产访问日志的统计,不是经验直觉。
4. 动手:用 k6 跑第一次负载测试
4.1 准备一个被测服务
任何本地 HTTP 服务都行。最快的办法是起一个公开的练习靶站(如
https://test.k6.io),或本地 docker run 一个 nginx。第一次练习
不要压公司的真实环境——压测本质是制造故障,打到生产等于事故。
4.2 写第一个场景
k6(Grafana 开源)用 JavaScript 写场景,安装后一个文件就能跑:
// loadtest.js:模拟 20 个虚拟用户持续访问 30 秒
import http from 'k6/http';
import { check } from 'k6';
export const options = {
vus: 20, // 虚拟用户数(并发)
duration: '30s',
thresholds: { // 及格线:不达标则命令退出码非 0,可直接进 CI
http_req_failed: ['rate<0.01'], // 错误率 < 1%
http_req_duration: ['p(95)<500'], // P95 < 500ms
},
};
export default function () {
const res = http.get('https://test.k6.io/');
check(res, {
'状态码 200': (r) => r.status === 200,
'响应包含标题': (r) => r.body.includes('test.k6.io'),
});
}k6 run loadtest.js4.3 读懂输出
跑完终端会给出各指标的统计,重点三行:
http_req_duration..............: avg=180ms min=95ms med=170ms max=890ms p(90)=310ms p(95)=402ms
http_req_failed................: 0.00%
iterations.....................: 3241 107.9/sp(95)=402ms:95% 的请求在 402ms 内完成——这才是”用户体感”的口径。http_req_failed:失败率 0%,check的断言失败也算在内。iterations后面的107.9/s就是实测吞吐量。
把 vus 从 20 逐步调到 50、100、200,记录每个档位的 P95 和吞吐量:
吞吐量还在涨,说明没到瓶颈;吞吐量不涨而延迟暴涨,说明到了。
这条曲线就是容量规划的原始材料。阶梯加压找拐点的完整方法论见
《压力测试与稳定性测试》。
4.4 目标值从哪来
上面的 p(95)<500 是编的。真实项目的及格线来自三处,优先级从高到低:
- 业务 SLO:产品/运维约定的服务目标(与《监控与可观测性》里的 SLO 是同一套数,压测验收线应当与线上 SLO 一致)。
- 生产实测基线:从监控里取当前 P95,留出 30% 到 50% 余量。
- 通用经验参考线(响应时间 P95 < 500ms、错误率 < 0.1%、CPU < 70%) ——只在什么数据都没有时用于起步。
5. 工具地图:k6、JMeter、LoadRunner
三款工具覆盖了从开源到商业的光谱,选型看团队栈和预算:
| 工具 | 形态 | 强项 | 适合 |
|---|---|---|---|
| k6 | JS 脚本、单二进制 | 阈值即代码、CI 集成顺滑 | API 压测、流水线内 |
| JMeter | GUI 编排、Java | 协议广、生态老牌 | 复杂场景、已有积累 |
| LoadRunner | 商业套件 | 协议最全、企业级分析与报告 | 大型企业、特殊协议 |
本模块《JMeter》专讲 JMeter 的线程组、采样器与分布式压测,这里只给 LoadRunner 留一个概念锚点——它的三个核心词汇在任何工具里都有对应物:
- 事务(transaction):把一组请求包成一个计时单元,对应 k6 的
group或 JMeter 的事务控制器; - 思考时间(think time):模拟用户操作间的停顿,不加它测出的是 “机器人极限”而非真实负载;
- 参数化(parameterization):每个虚拟用户用不同账号/数据,避免 缓存让结果失真。LoadRunner 脚本用 C 写,事务与思考时间长这样:
Action()
{
lr_start_transaction("login"); // 事务开始计时
web_submit_data("login",
"Action=https://api.example.com/login",
"Method=POST", ITEMDATA,
"Name=username", "Value={username}", ENDITEM, // {username} 是参数化
"Name=password", "Value={password}", ENDITEM,
LAST);
lr_think_time(3); // 模拟用户填表单的 3 秒
lr_end_transaction("login", LR_PASS); // 事务结束
return 0;
}6. 坑点与自检
- 用 GUI 模式施压。JMeter 图形界面自身扛不住上千并发,数据失真。
施压一律命令行非 GUI 模式(
jmeter -n -t plan.jmx -l result.jtl), GUI 只用来编脚本。 - 压测环境与生产差异过大。单机小内存容器压出的”极限”毫无参考 价值。环境配置(机器规格、数据库数据量、网络)应尽量对齐生产, 至少按比例缩放并记录差异。
- 忘了思考时间与参数化。所有虚拟用户拿同一账号疯狂连发,测出的 是缓存与锁的极限,不是用户负载。参数化 + 思考时间是默认项。
- 只压单接口。真实流量是接口组合与数据关联(登录、浏览、下单、 支付)。单接口达标不等于整链路达标,核心路径要按比例混合压。
- 压测没有监控。只看压测工具的响应时间,不看服务端 CPU/内存/ 连接数,崩了也不知道瓶颈在哪一层。压测时服务端监控必须同时开着。
- 对生产环境直接压。没有隔离环境、没有限流保护、没有干路预案 就对生产施压,等于自己制造事故。
自检清单:
- 能说出当前项目四类性能测试分别的目标与加压方式
- 响应时间口径是 P95/P99,且及格线能追溯到 SLO 或生产基线
- 并发数估算来自访问日志统计而非拍脑袋
- 压测脚本有参数化与思考时间,施压用非 GUI 模式
7. 练习
- 对
https://test.k6.io依次用 vus=1/10/50/100 各跑 30 秒,记录 P95 与吞吐量,画出”并发 - 吞吐量”关系,标出吞吐量不再增长的拐点。 - 给第 1 题的脚本加
thresholds,把 P95 及格线设为你实测基线的 1.3 倍,然后故意把 vus 加到远超拐点,观察阈值失败时的退出码。 - 用访问日志(或现成统计)估算你所在项目高峰期的真实并发数,对照 当前压测覆盖的并发档位,评估缺口。
8. 下一步
- JMeter 线程组、采样器、分布式施压:见《JMeter》。
- 阶梯加压找拐点、内存泄漏观察、稳定性方法论:见《压力测试与稳定性测试》。
- 接口功能测试的三层断言与异常语义:见《接口测试方法》。
- 接口自动化进 CI 与契约测试:见《API 自动化测试》《API 测试工具链与契约测试》。
- 压测指标与线上监控对齐:见《监控与可观测性》(devops 模块)。