性能测试这件事,说到底是给系统"上强度"的过程。平时跑得好好的服务,一旦并发上来,响应时间飙升、错误率暴涨、数据库连接池打满,这类问题在功能测试阶段几乎不可能暴露。所以每个测试工程师的武器库里,都得备上几款趁手的压测工具。2026 年这个时间点,压测工具的格局其实已经比较清晰了:JMeter 依然是绕不开的老大哥,k6 靠着脚本化和云原生友好度快速上位,Locust 用 Python 写压测脚本的路子也圈了一大批人。但除了这三款,还有不少在特定场景下更顺手的工具,比如做协议级极限压测的 wrk、做浏览器真实用户模拟的 Playwright 压测方案、做全链路压测的阿里系工具等。
这篇盘点不打算写成产品说明书,而是从"什么场景该用哪款"的角度切入,把 13 款主流压测工具的定位、核心能力、上手门槛、踩坑点讲清楚。无论你是刚接触性能测试的新人,还是已经用 JMeter 压过几十轮的老手,都能从中找到适合自己的那一款。文章会重点拆解 JMeter 的完整使用链路(安装、脚本录制、参数化、断言、报告汉化),也会把 k6、Locust 这类脚本化工具的选型逻辑讲透,最后给出一套按场景选工具的决策方法。
1. 压测工具到底在解决什么问题
1.1 从"功能正常"到"扛得住"之间的鸿沟
功能测试验证的是"这个按钮点下去有没有反应",性能测试验证的是"一万个人同时点这个按钮,系统还活不活得下来"。这两件事的难度完全不在一个量级。功能测试可以慢慢点、一个个点,性能测试必须在极短时间内制造出大量并发请求,还要精确控制请求的节奏、参数、关联关系,同时收集响应时间、吞吐量、错误率等指标。
压测工具的核心价值就在于此:它把"制造并发"这件事标准化了。你不需要自己写多线程代码去发请求,工具帮你管理线程池、连接池、请求队列,你只需要描述"要发什么请求、发多少、怎么发"。听起来简单,但真正用起来,坑都在细节里——比如 JMeter 的线程组参数怎么设、k6 的 VU 和迭代次数什么关系、Locust 的 wait_time 怎么配才合理,这些直接决定了压测结果有没有参考价值。
1.2 压测工具的能力分层
市面上的压测工具,按能力可以粗略分成三层。第一层是协议级压测工具,直接构造 HTTP/TCP/UDP 等协议请求,追求极致的并发能力和资源效率,代表是 wrk、hey、ab。第二层是场景级压测工具,能模拟完整的用户业务流程,支持参数化、关联、断言、事务控制,代表是 JMeter、LoadRunner、Locust、k6。第三层是全链路压测平台,在生产环境做影子流量压测,代表是阿里云 PTS、腾讯云压测大师这类云服务。
选工具之前先想清楚你要压什么。如果只是想知道某个接口的 QPS 上限,wrk 几行命令就搞定;如果要模拟"登录-浏览-加购-下单"的完整链路,还得看 JMeter 或 k6;如果要在生产环境做全链路验证,那基本只能上云平台。搞错层次,要么杀鸡用牛刀,要么压出来的数据根本不能用。
1.3 一个容易被忽略的前提:压测环境
工具选得再好,环境不对,结果全是废的。我见过太多团队在开发机上压测,然后拿着数据去评估生产容量,这跟拿体温计量身高一样离谱。压测环境至少要满足几个条件:网络链路和生产一致(至少带宽不能差太多)、数据库数据量级接近生产、中间件配置和生产对齐、压测机本身不能成为瓶颈。
压测机成为瓶颈这件事特别隐蔽。JMeter 默认用 Java 堆内存跑,如果压测机 CPU 先跑满了,你看到的响应时间飙升其实是压测机自己扛不住,不是被测系统的问题。所以压测前一定要先确认压测机的资源水位,通常建议压测机 CPU 不超过 70%,否则数据不可信。
2. JMeter:绕不开的压测主力
2.1 为什么 JMeter 能稳坐头把交椅
JMeter 是 Apache 基金会的项目,纯 Java 写的,跨平台,开源免费。它最大的优势是生态完整:插件市场里有 MQTT、gRPC、WebSocket、JDBC 等各种协议的扩展,几乎你能想到的协议都有人做过插件。再加上 GUI 界面操作,对不写代码的测试人员非常友好,录制脚本、参数化、断言、报告生成都有现成的组件。
但 JMeter 的 GUI 模式只适合调试脚本,真正压测必须用命令行模式(jmeter -n -t test.jmx -l result.jtl)。GUI 模式本身会消耗大量资源,用它压测等于让压测机自己拖自己后腿。这个坑每年都有新人踩,记住一句话:GUI 调脚本,命令行压测。
2.2 JMeter 安装:Windows 和 Linux 的差异
Windows 上装 JMeter 相对简单,去官网下载 zip 包解压,配好JAVA_HOME和JMETER_HOME环境变量,把bin目录加到PATH里就能用。但要注意 JDK 版本,JMeter 5.6 以上建议用 JDK 17,用 JDK 8 虽然也能跑,但某些插件会报兼容性错误。
Linux 上装 JMeter 通常是压测机场景,步骤类似但有几个细节。第一,不要用 root 跑 JMeter,权限问题会导致某些文件写不进去。第二,jmeter-server的端口要提前放行,分布式压测时 master 和 slave 之间靠这个端口通信。第三,jmeter.properties里的server.rmi.ssl.disable建议设为 true,否则分布式压测的证书配置能把人折腾疯。
# Linux 安装 JMeter 的典型步骤 wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.6.3.tgz tar -xzf apache-jmeter-5.6.3.tgz -C /opt/ echo 'export JMETER_HOME=/opt/apache-jmeter-5.6.3' >> ~/.bashrc echo 'export PATH=$JMETER_HOME/bin:$PATH' >> ~/.bashrc source ~/.bashrc jmeter -v # 验证安装2.3 脚本录制:HTTPS 场景的正确姿势
JMeter 录制脚本有两种方式:HTTP(S) Test Script Recorder 和第三方工具(如 BlazeMeter 插件)。录制 HTTPS 脚本的核心是证书问题。JMeter 会生成一个自签名证书ApacheJMeterTemporaryRootCA.crt,放在bin目录下,你需要把这个证书导入到浏览器或系统的信任证书列表里,否则录制时浏览器会拦截请求。
具体步骤:启动 JMeter 的录制器,它会提示证书生成位置;在浏览器里导入这个证书到"受信任的根证书颁发机构";设置浏览器代理为localhost:8888;开始录制。录制完成后记得关掉代理,否则浏览器上不了网。这个流程听起来简单,但证书导入的位置选错(比如导到了"个人"而不是"受信任的根证书"),录制就会一直失败,排查起来很费时间。
2.4 参数化:让压测数据不再千篇一律
压测最忌讳所有请求用同一份数据,这样压出来的结果没有代表性,还容易触发缓存导致数据虚高。JMeter 的参数化有几种方式:CSV Data Set Config、用户定义的变量、函数助手(如__Random、__UUID)、JDBC Request 从数据库取值。
CSV Data Set Config 是最常用的,把测试数据放在 CSV 文件里,每个线程读一行。要注意Sharing mode的设置:All threads是所有线程共享一个文件指针,Current thread是每个线程独立读文件。如果 CSV 行数少于线程数,还要勾选Recycle on EOF,否则线程读到文件末尾就报错了。
JDBC Request 参数化是进阶玩法,适合数据需要从数据库动态取的场景。比如压测下单接口,需要真实的用户 ID,就可以先用 JDBC Request 查出用户 ID 列表,再用 ForEach 控制器遍历。这里有个坑:JDBC Request 查出的结果集,变量名要配合Result variable name使用,取值时用${变量名_1}、${变量名_2}这种格式,索引从 1 开始,不是从 0。
2.5 断言:Beanshell 断言和响应断言怎么选
JMeter 的断言组件很多,最常用的是响应断言(Response Assertion)和 Beanshell 断言。响应断言适合简单的文本匹配,比如判断响应里有没有"success"字样。Beanshell 断言适合复杂逻辑,比如解析 JSON 后判断某个字段的值。
Beanshell 断言的性能是个隐患。Beanshell 是解释执行的,每个请求都要跑一遍脚本,高并发下会明显拖慢压测机。如果断言逻辑不复杂,优先用 JSON Assertion 或 JSR223 Assertion(选 Groovy 语言),Groovy 编译后执行,性能比 Beanshell 好很多。这个细节在压测机资源紧张时特别重要,我见过因为 Beanshell 断言写太复杂,导致压测机 CPU 先跑满的案例。
2.6 HTML 报告汉化:让报告更易读
JMeter 命令行压测后会生成.jtl结果文件,用jmeter -g result.jtl -o report可以生成 HTML 报告。默认报告是英文的,汉化需要改bin/report-template目录下的模板文件。主要改content目录里的.ftl文件,把里面的英文标签替换成中文。
汉化这件事本身不难,但要注意编码问题。模板文件默认是 UTF-8,改的时候别用 GBK 保存,否则报告里会出现乱码。另外,JMeter 升级后模板文件会被覆盖,汉化前先备份一份,升级后重新覆盖回去。
3. k6:脚本化压测的新势力
3.1 k6 的设计哲学:代码即压测
k6 是 Grafana 家的开源压测工具,用 Go 写的,压测脚本用 JavaScript 写。它的设计理念和 JMeter 完全不同:JMeter 是 GUI 配置驱动,k6 是代码驱动。你写一个 JS 文件,定义options(并发数、持续时间、阈值)和default函数(每个 VU 执行的逻辑),然后k6 run script.js就跑起来了。
这种方式的优势很明显:脚本可以进 Git 版本管理,可以 code review,可以 CI/CD 集成。对于有开发背景的测试人员,k6 的上手速度比 JMeter 快得多。而且 k6 的资源效率比 JMeter 高,同样配置的压测机,k6 能压出更高的并发。
3.2 VU 和迭代次数:k6 的核心概念
k6 里有两个容易混淆的概念:VU(Virtual User)和迭代(Iteration)。VU 是虚拟用户数,迭代是每个 VU 执行default函数的次数。options里可以配vus和duration(按时间压),也可以配vus和iterations(按次数压)。
// 按时间压:50 个 VU 持续压 30 秒 export const options = { vus: 50, duration: '30s', }; // 按次数压:50 个 VU 总共执行 1000 次 export const options = { vus: 50, iterations: 1000, }; export default function () { // 每个 VU 执行的请求逻辑 const res = http.get('https://example.com/api'); check(res, { 'status is 200': (r) => r.status === 200, 'response time < 500ms': (r) => r.timings.duration < 500, }); }check是 k6 的断言机制,它不会中断测试,只是记录通过率。如果要让断言失败时中断,得用throw或者配thresholds。thresholds是 k6 的特色功能,可以定义性能指标的红线,比如http_req_duration: ['p(95)<500']表示 95 分位响应时间必须小于 500ms,不满足就返回非零退出码,非常适合 CI 集成。
3.3 k6 的短板和应对
k6 最大的短板是协议支持不如 JMeter 全。它原生只支持 HTTP/1.1、HTTP/2、WebSocket、gRPC,像 MQTT、JDBC 这些需要靠扩展(xk6)自己编译。编译 xk6 扩展需要 Go 环境,对纯测试人员有门槛。
另一个问题是 k6 的脚本调试不如 JMeter 直观。JMeter 有 GUI 可以看请求树、看响应结果,k6 只能靠console.log和--http-debug参数。不过 k6 的--http-debug=full能打印完整的请求和响应,调试起来也不算太痛苦。
4. Locust:用 Python 写压测脚本
4.1 Locust 的定位:Python 生态的压测方案
Locust 是 Python 写的压测工具,压测脚本也是 Python。它的核心概念是User类和task装饰器:你定义一个继承HttpUser的类,用@task装饰器标记要执行的任务,wait_time控制任务之间的间隔。
from locust import HttpUser, task, between class WebsiteUser(HttpUser): wait_time = between(1, 3) # 每个任务之间等 1-3 秒 @task(3) # 权重 3,执行频率更高 def view_homepage(self): self.client.get("/") @task(1) # 权重 1 def view_about(self): self.client.get("/about")Locust 的优势是 Python 生态,如果你团队里都是 Python 开发者,用 Locust 写压测脚本几乎没有学习成本。而且 Locust 支持分布式压测,master 节点下发任务,worker 节点执行,扩展性不错。
4.2 wait_time 的设置逻辑
wait_time是 Locust 里最容易被忽视但影响很大的参数。它模拟的是真实用户操作之间的思考时间。设得太短,压测压力偏大,可能压出实际不会出现的瓶颈;设得太长,压力偏小,测不出系统的真实上限。
常见的设置是between(1, 3),即 1 到 3 秒之间随机。这个值要根据实际业务场景调整。比如压测登录接口,用户输完账号密码到点登录,思考时间可能就 2-3 秒;压测搜索接口,用户输入关键词到点搜索,可能就 1 秒左右。如果拿不准,可以先设一个保守值,跑一轮看结果再调。
4.3 Locust 的 Web UI 和命令行
Locust 自带 Web UI,启动时加--web-host和--web-port参数,浏览器打开就能看到实时压测数据,还能动态调整并发数。这个功能在压测过程中调参很方便,不用重启压测。
命令行模式适合 CI 集成,用--headless -u 100 -r 10 -t 5m表示无界面、100 个用户、每秒启动 10 个、持续 5 分钟。-r参数控制用户启动速率,这个值设太大,压测一开始就是尖峰流量,可能把系统直接打挂;设太小,压测时间被拉长,效率低。一般建议-r设为总用户数的 10% 左右。
5. 协议级压测工具:wrk、hey、ab 的适用边界
5.1 wrk:单机压测的性能天花板
wrk 是 C 写的 HTTP 压测工具,用 epoll 做事件驱动,单机就能压出几十万 QPS。它的用法极简:wrk -t12 -c400 -d30s http://example.com,12 个线程、400 个连接、压 30 秒。
wrk 的定位是测接口极限,不是测业务场景。它不支持参数化、关联、断言,就是一个 URL 反复压。所以它适合在开发阶段快速摸清某个接口的性能上限,或者做压测机的基准测试。wrk 还支持 Lua 脚本扩展,可以自定义请求逻辑,但写 Lua 的门槛比写 JS 或 Python 高不少。
5.2 hey 和 ab:轻量级的选择
hey 是 Go 写的,用法类似 wrk,hey -n 10000 -c 100 http://example.com,发 10000 个请求、100 并发。它的输出比 wrk 更详细,有响应时间的直方图。ab(ApacheBench)是 Apache 自带的,几乎每个 Linux 系统都有,ab -n 1000 -c 100 http://example.com就能跑。
ab 的问题是单线程的,压不出高并发,而且不支持 HTTPS 的 SNI,压 HTTPS 站点经常报错。hey 比 ab 强,但生态和扩展性不如 wrk。这三款工具的共同点是:只适合简单接口的快速压测,不适合复杂业务场景。
5.3 协议级工具的常见误用
最常见的误用是拿 wrk 或 ab 的压测结果去评估整个系统的容量。比如用 ab 压了一个查询接口,QPS 到 5000,就认为系统能扛 5000 并发用户。这是错的,因为真实用户不会只调一个接口,一个下单操作可能涉及十几个接口调用,还有数据库写入、消息队列、缓存更新。协议级工具压出来的只是单个接口的极限,不是系统的承载能力。
6. 云原生时代的压测新选择
6.1 Kubernetes 环境下的压测挑战
现在很多系统跑在 K8s 上,压测面临新问题:Pod 是动态扩缩的,压测时怎么保证压力均匀打到所有 Pod?服务发现怎么配?压测流量怎么和正常流量隔离?
JMeter 和 k6 都能跑在 K8s 里,但配置起来有讲究。JMeter 分布式压测在 K8s 里通常用 StatefulSet 部署 slave,master 用 Job 或 Pod 跑。k6 有官方的 k6-operator,用 CRD 定义压测任务,K8s 自动调度。Locust 也有 Helm Chart,可以快速部署分布式压测集群。
6.2 云压测平台的价值
阿里云 PTS、腾讯云压测大师这类云压测平台,核心价值是免运维的压测机集群和全链路压测能力。你不用自己准备压测机,平台按需分配,压完就释放。全链路压测可以在生产环境做,用影子表、影子库隔离压测数据,压测流量打上特殊标记,不影响真实用户。
云平台的缺点是贵,而且压测脚本要适配平台的格式。如果只是偶尔压测,用开源工具自己搭更划算;如果是要做常态化的生产环境压测,云平台的投入是值得的。
6.3 从单节点到云上的迁移压测案例
有个典型的场景:单节点 K8s 上的微服务整套环境,要迁移到云上 ECS,迁移后需要验证云上环境的承载能力。这种场景下,压测方案通常是:迁移前在本地用 JMeter 脚本压一轮,记录基线数据;迁移后在云上用同样的脚本压一轮,对比两轮数据。压测脚本要提前准备好,包括参数化数据、断言逻辑、事务控制器。
迁移压测的关键是环境对齐。云上的 ECS 配置、网络带宽、数据库规格如果和本地不一致,压测结果就没有可比性。另外,迁移后的压测最好由专门的压测人员执行,避免开发和运维自己压自己,数据不够客观。
7. 13 款工具的场景化选型对照
7.1 按压测目标选工具
| 压测目标 | 推荐工具 | 理由 |
|---|---|---|
| 单接口极限 QPS | wrk、hey | 资源效率高,单机压出高并发 |
| 复杂业务流程 | JMeter、k6、Locust | 支持参数化、关联、断言、事务 |
| CI/CD 集成 | k6、Locust | 脚本化,命令行友好,退出码可控 |
| 生产环境全链路 | 云压测平台 | 影子流量隔离,不影响真实用户 |
| 浏览器真实用户模拟 | Playwright + 压测框架 | 能执行 JS、渲染页面 |
| MQTT/gRPC 等协议 | JMeter(插件)、k6(xk6) | 协议扩展支持 |
| 快速冒烟压测 | ab、hey | 系统自带或单文件,零配置 |
7.2 按团队技术栈选工具
团队以 Java 为主,JMeter 是自然选择,生态和团队技能匹配。团队以 Python 为主,Locust 更顺手。团队以 JS/TS 为主,k6 的学习成本最低。团队有 Go 背景,可以试试 Vegeta 或 hey。工具选型不要只看功能,还要看团队能不能维护压测脚本,脚本没人维护,再好的工具也是摆设。
7.3 按压测频率选工具
一次性压测,用最顺手的工具快速出结果就行。常态化压测,要考虑脚本的版本管理、CI 集成、报告归档。JMeter 的.jmx文件是 XML,diff 起来不友好;k6 和 Locust 的脚本是纯代码,Git 管理更自然。如果压测要进 CI,k6 的thresholds和退出码机制是最成熟的。
8. 压测实操中的那些坑
8.1 压测机资源监控不能省
压测时一定要监控压测机的 CPU、内存、网络。JMeter 压测机 CPU 超过 80%,压测结果就开始失真。k6 虽然资源效率高,但 VU 数开太大也会吃满 CPU。压测前先用top、vmstat、sar看一眼压测机状态,压测过程中也要持续监控。
8.2 参数化数据的真实性
参数化数据要尽量接近真实。用user1、user2这种假数据,可能全部命中同一份缓存,压出来的 QPS 虚高。用真实分布的数据,比如从生产脱敏后的用户 ID,压测结果才有参考价值。另外,参数化文件的行数要足够,如果 1000 个线程读 100 行数据,每个线程平均读 10 次,缓存命中率会很高。
8.3 断言失败的处理策略
压测中断言失败,是继续压还是停止?这取决于压测目的。如果是测系统极限,断言失败可以继续,记录错误率就行。如果是验证功能正确性,断言失败应该停止,因为后续请求可能都是无效的。JMeter 里可以用Stop Thread或Stop Test来控制,k6 里用throw或abort。
8.4 报告解读的常见误区
平均响应时间是最容易误导人的指标。100 个请求,99 个 10ms,1 个 10s,平均响应时间是 110ms,看起来还行,但实际上有 1% 的用户体验极差。所以看报告一定要看分位数,P95、P99 比平均值有意义得多。吞吐量和响应时间要一起看,吞吐量高但响应时间长,说明系统在超负荷运转。
8.5 分布式压测的时钟同步
分布式压测时,master 和 slave 的时钟必须同步,否则结果文件里的时间戳对不上,报告生成会出错。Linux 上用 NTP 同步时间,压测前ntpdate一下。JMeter 分布式压测还要注意 slave 的jmeter-server进程要提前启动,master 的remote_hosts配置要写对 IP 和端口。
9. 从脚本到报告:一条完整的压测链路
9.1 脚本开发阶段的检查清单
脚本写完先别急着压,过一遍检查清单:请求的 URL 和参数对不对、关联的变量有没有取到值、断言逻辑是否合理、事务控制器有没有加、思考时间设了没有、参数化文件路径是不是绝对路径。这些检查能避免压测跑了一半发现数据全是错的。
9.2 压测执行阶段的节奏控制
压测不要一上来就满并发,要阶梯式加压。先用 10% 的并发跑一轮,看系统反应;没问题加到 50%;再没问题加到 100%。阶梯加压能观察到系统在哪个压力点开始出现性能拐点,这个拐点比极限 QPS 更有价值。
9.3 报告归档和对比
每轮压测的报告要归档,标注压测时间、环境、脚本版本、参数配置。不同轮次的报告要能对比,才能看出优化有没有效果。JMeter 的 HTML 报告、k6 的 summary 输出、Locust 的 CSV 结果,都要统一归档到版本库或文档系统里。
压测这件事,工具只是手段,核心是用数据说话。选对工具、配对环境、读懂报告,这三步做到位,压测才能真正帮到系统稳定性。工具年年有新的,但压测的基本逻辑不会变:制造压力、观察反应、定位瓶颈、验证优化。把这套逻辑跑通,用什么工具都是顺手的事。