性能压测工具横评:JMeter vs wrk vs Gatling vs k6——该用哪个
引言
去年大促前,我们给订单服务做了一轮压测。报告很漂亮:
8,000 QPS,平均响应时间 38ms,错误率 0%。
老板批了扩容预算,团队安心等大促。结果开场第 7 分钟,订单服务在 1,200 QPS 就开始超时,网关 504 一片。
复盘会上有人问了一句:"那 8,000 QPS,到底是订单服务扛住的,还是压测机扛住的?"
会议室安静了。
把脚本和监控翻出来,四个问题摆上桌:
- 压测是同事在 JMeter GUI 里点的,本机 16 核开了 2,000 个线程,压测机 CPU 先到 100%,GUI 还在实时刷新结果树;
- 2,000 线程从第一秒压到最后一秒,没有阶梯、没有预热,JIT 和连接池冷启动阶段的数据被平均值抹平了;
- 只看平均响应时间 38ms,没人提 P99——事后翻原始数据,当时 P99 已经 2.3 秒;
- 所有请求都用同一个商品 ID,缓存命中率接近 100%,测出来的是"缓存有多快",不是"系统能扛多少"。
那次之后我才真正理解:压测工具决定你能不能看清瓶颈,压测方法决定你看到的是不是真相。
这篇文章把四款主流工具放在同一张桌子上:JMeter、wrk、Gatling、k6。不吹"谁最强"——它们压根不是同一类东西。
| 工具 | 一句话定位 | 最适合 | 最不适合 | 上手成本 |
|---|---|---|---|---|
| JMeter | 图形化、插件最全的"压测 IDE" | 复杂业务链路、多协议、非程序员参与 | 极限吞吐、CI 门禁 | 低(GUI)/ 中(CLI) |
| wrk | 几十 KB 的 HTTP 压力发生器 | 接口基线、网关调优、快速验证 | 业务链路、断言、HTTP/2 | 极低 |
| Gatling | 用代码描述用户行为的 DSL | 复杂场景、漂亮报告、JVM 团队 | 临时压测、轻量级验证 | 中高(Scala) |
| k6 | 为 CI 而生的 JS 压测工具 | 门禁、云原生、Grafana 生态 | 极限吞吐、无代码团队 | 低中(JS) |
一句话破题:JMeter 是瑞士军刀,wrk 是手术刀,Gatling 是可编程的流水线,k6 是写进 CI 的压测代码——先想清楚你要回答的是"极限数字""复杂链路"还是"每次合入都不许变慢",再选工具。选错了,压出来的数字连自己都不敢信。
一、四个工具,四种出身
1.1 JMeter:功能最全,也最"重"
出身:Apache 基金会顶级项目,Java 编写,历史可以追溯到 1998 年。
定位:把压测做成"可视化编程"——线程组、取样器、断言、定时器、监听器,拖拖拽拽就能拼出一条业务链路。
特点:
- 协议覆盖最广:HTTP、JDBC、JMS、FTP、LDAP、SMTP、TCP,WebSocket 和 gRPC 靠插件补齐
- 插件生态最成熟:Custom Thread Groups、PerfMon、Backend Listener、各类取样器
- 支持分布式压测(controller / worker)
- 内置 HTML 报告,含 APDEX、分位数、吞吐曲线
代价:
- 一个虚拟用户就是一个 Java 线程,并发一高,压测机自己先吃不消
.jmx是 XML,Git diff 几乎不可读,Code Review 无从谈起- GUI 只能用来编脚本,真压测必须走 CLI——这条坑每年都有人踩
# 正确的压测姿势:非 GUI 模式 + 结束后生成 HTML 报告
jmeter -n -t order.jmx -l result.jtl -e -o report/
1.2 wrk:一把只做一件事的手术刀
出身:Will Glozer 2012 年开源,C 语言 + epoll/kqueue + LuaJIT,代码量很小。
定位:只压 HTTP/1.1,只做"用固定连接数打满目标"这一件事。
特点:
- 极轻:单机几万到几十万并发连接,内存占用仍然很低
- 极快:没有 GC、没有虚拟机,压测端自身开销接近理论下限
- 可用 Lua 脚本做参数化和简单逻辑
- 一条命令出结果,适合随手验证
硬伤:
- 只支持 HTTP/1.1,HTTP/2 完全不支持
- 没有阶梯加压、没有到达率控制、没有断言、没有 HTML 报告
- 没有分布式能力
- 项目基本停更(4.2.0 之后多年没有新版本)
wrk -t8 -c256 -d30s --latency http://127.0.0.1:8080/api/orders/1
1.3 Gatling:把压测写成代码
出身:2011 年开源,Scala 编写,基于 Netty 的异步非阻塞引擎,开源版 Apache 2.0。
定位:用 DSL 描述用户行为,把压测脚本当成正式代码来维护。
特点:
- 虚拟用户是"消息"而不是线程,少量线程就能驱动大量并发
- 场景编排是一等公民:
inject、pause、feed、check、assert - HTML 报告是四家里最漂亮的,分位数、时序图、错误分布一应俱全
- 断言失败会让构建失败,天生适合 CI
- 3.7 之后提供 Java DSL,不想写 Scala 的团队也能用
代价:
- 要建工程、要编译,临时压一个接口显得笨重
- Scala DSL 有学习曲线,报错信息对新手不友好
- 分布式压测要上商业版 Gatling Enterprise
- 插件生态比 JMeter 小一个量级
mvn gatling:test
# 或
gatling.sh -s com.example.OrderSimulation
1.4 k6:开发者体验优先
出身:LoadImpact 2017 年开源,后被 Grafana Labs 收购,Go 编写,脚本用 JavaScript。
定位:让压测脚本像单元测试一样进入研发流程。
特点:
- 单二进制,装完就能跑,没有 JVM、没有依赖
- 脚本是 JS,前端和后端都能改
- 原生支持场景编排、到达率模型、阈值门禁
- 原生支持 HTTP/1.1、HTTP/2、WebSocket、gRPC,浏览器级测试有
k6/browser - 输出可以直接打到 Prometheus、InfluxDB、Grafana Cloud
- Kubernetes 上有官方 k6 Operator
代价:
- 每个 VU 跑一个 JS 运行时,压测端吞吐明显低于 wrk 和 Gatling
- 内置 HTML 报告要自己写
handleSummary - 特殊协议要靠 xk6 自己编译扩展
- AGPL-3.0 许可证,商用嵌入前要让法务看一眼
k6 run script.js
二、评测方法:先把"公平"定义清楚
压测工具横评最容易犯的错,是拿 A 的极限去比 B 的默认配置。所以先把口径钉死。
2.1 测试环境
| 项目 | 配置 |
|---|---|
| 压测机 | 8 vCPU / 16GB,Linux,万兆网卡 |
| 目标机 | 8 vCPU / 16GB,同可用区 |
| 目标服务 | Nginx 返回 1KB 静态 JSON,开启 keep-alive |
| 并发 | 256(四个工具统一) |
| 时长 | 60 秒(前 10 秒预热,取后 50 秒) |
| 指标 | 压测端吞吐、压测机 CPU、客户端观测 P99、错误率 |
目标服务选 Nginx 而不是业务服务,是为了让瓶颈尽量落在压测端——这样比较的才是"工具自身开销",而不是"谁的 Java 服务写得更快"。
2.2 统一场景
登录拿 token → 查订单 → 停顿 1 秒 → 循环。并发从 0 阶梯到 200,保持 5 分钟。
这个场景能同时考察四件事:多步骤会话、动态参数传递、think time、响应断言。
2.3 同一个场景,四种写法
JMeter:元件树就是脚本
Test Plan
├── Thread Group(线程数 200,Ramp-up 60s,循环 10)
│ ├── HTTP Cookie Manager
│ ├── HTTP Header Manager
│ ├── CSV Data Set Config(users.csv)
│ ├── Transaction Controller「登录」
│ │ ├── HTTP Request:POST /api/login
│ │ └── JSON Extractor:$.token → ${token}
│ ├── Transaction Controller「查订单」
│ │ ├── HTTP Request:GET /api/orders/${orderId}
│ │ ├── Response Assertion:200
│ │ └── JSON Assertion:$.status == "PAID"
│ └── Constant Timer:1000ms
└── Backend Listener(InfluxDB / Prometheus)
真正的脚本是一个几千行的 .jmx XML。改一个参数要在 GUI 里点半天,Review 时只能靠肉眼比对。
wrk:Lua 能做的事比想象中多,但也就到这里
-- tokens.txt:提前用登录接口批量换好的 token,每行一个
local tokens = {}
local idx = 0
function init(args)
for line in io.lines("tokens.txt") do
tokens[#tokens + 1] = line
end
end
request = function()
idx = idx + 1
local token = tokens[(idx % #tokens) + 1]
return wrk.format("GET", "/api/orders/1", {
["Authorization"] = "Bearer " .. token
})
end
注意两个细节:init 在每个 wrk 线程里各执行一次,token 表是线程私有的;wrk 没有会话概念,登录链路只能提前在脚本外准备好。多步骤业务链路,wrk 天生不擅长。
Gatling:场景、检查、注入、断言,都是一等公民
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class OrderSimulation extends Simulation {
val httpConf = http
.baseUrl("http://localhost:8080")
.acceptHeader("application/json")
val users = csv("users.csv").circular
val scn = scenario("登录并查询订单")
.feed(users)
.exec(http("登录")
.post("/api/login")
.body(StringBody("""{"user":"${user}","pass":"${pass}"}""")).asJson
.check(status.is(200))
.check(jsonPath("$.token").saveAs("token")))
.pause(1)
.exec(http("查询订单")
.get("/api/orders/${orderId}")
.header("Authorization", "Bearer ${token}")
.check(status.is(200))
.check(jsonPath("$.status").is("PAID")))
.pause(2, 5)
setUp(
scn.inject(
rampUsersPerSec(1).to(200).during(60.seconds),
constantUsersPerSec(200).during(300.seconds)
)
).protocols(httpConf)
.assertions(
global.responseTime.percentile3.lt(500),
global.successfulRequests.percent.gt(99.5)
)
}
k6:用 JS 把同一件事写一遍
import http from 'k6/http';
import { check, sleep } from 'k6';
const BASE = 'http://localhost:8080';
export const options = {
scenarios: {
login_and_query: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '60s', target: 200 },
{ duration: '5m', target: 200 },
],
},
},
thresholds: {
http_req_duration: ['p(99)<500'],
http_req_failed: ['rate<0.01'],
checks: ['rate>0.995'],
},
};
export function setup() {
const res = http.post(
`${BASE}/api/login`,
JSON.stringify({ user: 'u1', pass: 'p1' }),
{ headers: { 'Content-Type': 'application/json' } }
);
return { token: res.json('token') };
}
export default function (data) {
const res = http.get(`${BASE}/api/orders/1`, {
headers: { Authorization: `Bearer ${data.token}` },
});
check(res, {
'status is 200': (r) => r.status === 200,
'status is PAID': (r) => r.json('status') === 'PAID',
});
sleep(1);
}
setup() 只执行一次,返回的数据在所有 VU 之间只读共享——这是 k6 处理"登录一次、全员复用"的标准姿势。
2.4 度量口径
| 指标 | 怎么取 | 为什么重要 |
|---|---|---|
| 吞吐 | 成功响应数 / 有效时长 | 别把错误响应也算进吞吐 |
| P50 / P95 / P99 / P999 | 原始样本分位数 | 平均值会掩盖长尾 |
| 错误率 | 非 2xx + 超时 + 断言失败 | 高并发下的错误率比吞吐更早恶化 |
| 压测机 CPU / 软中断 | mpstat -P ALL 1、sar -n DEV 1 | 判断压测端有没有先饱和 |
| 目标机饱和度 | CPU、GC、连接池、线程池队列 | 找到真正的瓶颈 |
先记住一句:这组数字只用来比较"工具自身开销",不用来给目标服务下容量结论。 换一台机器、换一个响应体大小,绝对值会差几倍,但四个工具之间的相对关系比较稳定。
三、维度一:上手成本与表达力
3.1 第一条压测命令
| 工具 | 最短路径 | 到出结果 | 需要什么 |
|---|---|---|---|
| JMeter | 打开 GUI,建线程组 + HTTP 请求 + 监听器,点启动 | 5–10 分钟 | JDK |
| wrk | wrk -t8 -c256 -d30s URL | 10 秒 | 一个二进制 |
| Gatling | 建 Maven 工程、写 Simulation、mvn gatling:test | 30–60 分钟 | JDK + 构建工具 |
| k6 | 装二进制、写 20 行 JS、k6 run | 10–20 分钟 | 一个二进制 |
如果只是想知道"这个接口大概能扛多少",wrk 的投入产出比没有对手。JMeter 赢在"不写代码也能拼出复杂链路",但它的最短路径恰恰是最容易出事的那条(GUI 压测)。
3.2 复杂场景的表达力
| 能力 | JMeter | wrk | Gatling | k6 |
|---|---|---|---|---|
| 多步骤会话 | ✅ 元件组合 | ❌ 需脚本外预处理 | ✅ DSL | ✅ JS |
| 动态参数提取 | ✅ JSON/正则/XPath | ⚠ Lua 手写 | ✅ check + saveAs | ✅ res.json() |
| 数据驱动 | ✅ CSV Data Set | ⚠ Lua 读文件 | ✅ Feeder | ✅ SharedArray |
| 条件分支/循环 | ✅ Controller | ❌ | ✅ doIf / repeat | ✅ JS |
| 自定义指标 | ⚠ 需插件/脚本 | ❌ | ✅ | ✅ 自定义 Metric |
| 断言失败即失败 | ⚠ 需额外解析 | ❌ | ✅ | ✅ |
一个真实感受:用 JMeter 拼"登录 → 取 token → 下单 → 断言库存"这条链路,大概 20 个元件;用 Gatling 或 k6,是 30 行代码。前者上手快,后者改起来快——一次性脚本选 JMeter,长期维护的脚本选代码。
3.3 可维护性
| 维度 | JMeter | wrk | Gatling | k6 |
|---|---|---|---|---|
| 版本化 | ⚠ XML,diff 不可读 | ✅ Lua 文本 | ✅ 代码 | ✅ 代码 |
| Code Review | ❌ 基本没有 | ⚠ 脚本短,能看 | ✅ | ✅ |
| 复用/封装 | ⚠ Test Fragment | ❌ | ✅ 函数/类 | ✅ 模块 |
| 调试体验 | ⚠ 只能看结果树 | ⚠ 编译期报错 | ✅ console + --http-debug | |
| 与业务代码同仓 | ❌ | ⚠ | ✅ | ✅ |
这一维度决定了工具能不能长期活在团队里。压测脚本一旦变成"某人电脑上的一个 .jmx",下次大促前大概率要重写。
3.4 结论
上手速度:wrk > JMeter > k6 > Gatling
长期可维护性:Gatling ≈ k6 > wrk > JMeter
四、维度二:协议覆盖与场景编排
4.1 协议矩阵
| 协议 | JMeter | wrk | Gatling | k6 |
|---|---|---|---|---|
| HTTP/1.1 | ✅ | ✅ | ✅ | ✅ |
| HTTPS | ✅ | ✅ 需编译 OpenSSL | ✅ | ✅ |
| HTTP/2 | ⚠ 第三方插件 | ❌ | ⚠ 以当前版本为准 | ✅ 原生 |
| WebSocket | ✅ 插件 | ❌ | ✅ | ✅ |
| gRPC | ⚠ 社区插件 | ❌ | ✅ 3.9+ | ✅ 原生模块 |
| TCP/UDP | ✅ TCP | ❌ | ❌ | ⚠ xk6 扩展 |
| JDBC | ✅ | ❌ | ❌ | ⚠ xk6 扩展 |
| JMS / MQ | ✅ JMS | ❌ | ✅ JMS | ⚠ xk6 扩展 |
| MQTT | ✅ 插件 | ❌ | ⚠ 社区 | ⚠ xk6 扩展 |
| 浏览器 | ⚠ WebDriver Sampler | ❌ | ❌ | ✅ k6/browser |
这张表基本决定了选型的下限:
- 要压 JDBC、JMS、老协议 → 只有 JMeter 是稳妥选择
- 要压 HTTP/2 或 gRPC → k6 最省事
- 只想压 HTTP/1.1 接口 → 四个都能干,wrk 最轻
4.2 场景编排矩阵
| 能力 | JMeter | wrk | Gatling | k6 |
|---|---|---|---|---|
| 固定并发 | ✅ | ✅ | ✅ | ✅ |
| 阶梯加压 | ✅ 插件 | ❌ | ✅ 原生 | ✅ 原生 |
| 恒定到达率 | ✅ 插件/定时器 | ❌ | ✅ 原生 | ✅ 原生 |
| think time | ✅ Timer | ⚠ Lua delay() | ✅ pause | ✅ sleep |
| 参数化 | ✅ | ⚠ | ✅ | ✅ |
| 断言 | ✅ | ❌ | ✅ | ✅ |
| 阈值门禁 | ⚠ 自己解析 | ❌ | ✅ | ✅ |
| 分布式 | ✅ 自带 | ❌ | ⚠ 商业版 | ✅ Operator/Cloud |
| 录制/转换 | ✅ 代理录制 | ❌ | ✅ HAR | ⚠ HAR 工具 |
wrk 的"没有阶梯、没有到达率"不是实现偷懒,是设计取舍:它只做固定连接数的闭模型压测。你可以用多个 wrk 进程叠加出阶梯,但那是手工活,不是工具能力。
4.3 一个容易被忽略的差异:开模型 vs 闭模型
- 闭模型:固定 VU / 连接数,上一个请求完成后才发下一个。服务变慢 → 发压变慢 → 慢请求被"藏起来"
- 开模型:固定到达率,不管服务多慢都按节奏发。服务变慢 → 请求排队 → 延迟真实暴露
| 工具 | 默认模型 | 怎么切到开模型 |
|---|---|---|
| JMeter | 闭(线程) | Arrivals Thread Group、Precise Throughput Timer |
| wrk | 闭(固定连接) | 不支持;用 wrk2 的 -R 近似 |
| Gatling | 两种都支持 | constantUsersPerSec / rampUsersPerSec |
| k6 | 两种都支持 | constant-arrival-rate / ramping-arrival-rate |
这个差异比"谁快"重要得多,第六章详细讲。
4.4 结论
协议广度:JMeter > k6 > Gatling > wrk
场景编排:Gatling ≈ k6 > JMeter > wrk
五、维度三:压测端性能与资源开销
5.1 先搞清楚:你测的可能是压测机
压测系统的吞吐上限是:
实测吞吐 ≈ min(目标服务能力, 压测端发压能力, 网络带宽)
多数人只盯着第一项,忽略了后两项。当压测端先饱和时,你看到的"目标服务 P99 飙升"其实是压测机自己的排队。
四个工具的并发模型完全不同:
JMeter 1 VU = 1 个 Java 线程(阻塞 IO)
→ 线程栈 + 上下文切换 + GC,并发越高越重
wrk 1 线程 = 1 个 epoll 事件循环(固定线程数)
→ 无 GC、无虚拟用户对象,开销接近理论下限
Gatling 1 VU = 1 条消息(Netty 事件循环驱动)
→ 少量线程撑大量 VU,JVM 但非线程模型
k6 1 VU = 1 个 goroutine + 1 个 JS 运行时
→ 协程轻,但每条脚本都要过 JS 引擎
5.2 压测端吞吐实测
同一台 8 vCPU 压测机、同一个 Nginx 目标、256 并发、60 秒、1KB 响应:
| 工具 | 压测端吞吐(req/s) | 压测机 CPU | 客户端观测 P99 | 瓶颈在哪 |
|---|---|---|---|---|
| wrk 4.2 | 40–55 万 | 90–100% | 1.5–2.5ms | 接近目标上限 |
| Gatling 3.x | 18–30 万 | 80–95% | 2–4ms | 压测端 |
| k6 0.5x/1.x | 8–15 万 | 80–95% | 3–6ms | JS 运行时 |
| JMeter 5.6 | 2.5–5 万 | 90–100% | 8–15ms | 线程模型 |
三个读数:
- wrk 的吞吐就是"这台机器能发多快"的基准线。Gatling 大约是它的 50–60%,k6 约 20–30%,JMeter 约 5–10%。
- 同样 256 并发,JMeter 产生的实际 RPS 最低——不是它"测得不准",是它的线程在客户端侧就排队了,延迟读数被自身放大。
- 这些数字不是排行榜。换个响应体大小、换个 TLS、换个脚本复杂度,比例会变;但"wrk 最轻、JMeter 最重"这个排序很稳定。
5.3 资源占用
| 工具 | 运行形态 | 1,000 并发大致内存 | 10,000 并发 | 备注 |
|---|---|---|---|---|
| JMeter | JVM + 每 VU 一线程 | 2–4GB | 需多机或大堆调优 | 线程栈 + GC 是主要成本 |
| wrk | 固定线程 + epoll | 几十 MB | 通常 <1GB | 内存随连接数增长,增长很慢 |
| Gatling | JVM + 消息驱动 | 1–2GB | 数 GB,单机可撑 | 吞吐受 CPU 与网络限制 |
| k6 | Go + 每 VU 一个 JS VM | 1–3GB | 内存偏高,建议多实例 | 脚本越复杂,单 VU 越贵 |
以上是量级,不是精确值——脚本里有没有 JSON 解析、响应体多大、TLS 开不开,都会让数字翻倍。
5.4 压测机自检:别让工具背锅
正式压测前,先压一个"几乎不耗 CPU"的本地接口,确认压测端的天花板:
# 1. 压测端极限(目标换成本机一个空接口)
wrk -t8 -c256 -d30s --latency http://127.0.0.1:9999/ping
# 2. 压测机 CPU / 软中断
mpstat -P ALL 1
sar -n DEV 1
# 3. 连接数与文件描述符
ss -s
ulimit -n
# 4. 端口耗尽与 TIME_WAIT
sysctl net.ipv4.ip_local_port_range
ss -tan state time-wait | wc -l
经验值:目标 QPS 不要超过压测端极限的 50–60%。超过这条线,你测到的延迟里有一半是压测机自己的排队。
5.5 结论
压测端性能:wrk > Gatling > k6 > JMeter
但请注意:压测端性能只在"你要压的 QPS 接近工具上限"时才有意义。 压一个 5,000 QPS 的业务服务,四个工具都不会成为瓶颈,这时该看的是表达力和可维护性。
六、维度四:结果可信度与报告
6.1 报告能力
| 能力 | JMeter | wrk | Gatling | k6 |
|---|---|---|---|---|
| 实时进度 | ✅ | ✅ | ✅ | ✅ |
| HTML 报告 | ✅ 内置(APDEX/分位数/时序) | ❌ | ✅ 内置,最完整 | ⚠ 需 handleSummary |
| 原始样本 | ✅ .jtl | ❌ 只有汇总 | ✅ | ✅ JSON/CSV |
| 时序数据落库 | ✅ Backend Listener | ❌ | ⚠ 需插件 | ✅ Prometheus/InfluxDB |
| 分位数 | ✅ | ✅ --latency 直方图 | ✅ | ✅ 可配置 |
| 错误归因 | ✅ 断言结果 | ⚠ 只统计非 2xx | ✅ 错误分类 | ✅ 错误码 + check |
| 断言即门禁 | ⚠ | ❌ | ✅ | ✅ |
报告漂亮程度:Gatling > JMeter > k6 > wrk。
但报告漂亮不等于数字可信,接下来这个坑比报告重要十倍。
6.2 协调遗漏:一个把 P99 从 2 秒变成 200 毫秒的陷阱
闭模型压测里,VU 必须等上一个响应回来才能发下一个。服务端一慢,发压节奏自动变慢,最慢的那批请求反而采样最少——这就是 coordinated omission。
真实情况(假设每 100ms 该发一个请求):
t=0 发请求 A → 2000ms 才返回
t=100 本该发 B,但线程在等 A,没发
t=200 没发
...
t=2000 A 返回,记一笔 2000ms,然后发 B
B 很快返回 10ms
统计结果:
样本 = [2000, 10, 10, 10, ...]
大量本该在等待期间发出的请求根本没被记录
P99 被"稀释"成 200ms,真实用户体验是 2 秒
四个工具的处理方式:
| 工具 | 是否处理 | 怎么做 |
|---|---|---|
| wrk | ❌ 不处理 | 固定连接数的闭模型;用 wrk2 的 -R 按固定速率发压 |
| JMeter | ⚠ 默认不处理 | 换 Arrivals Thread Group / Precise Throughput Timer |
| Gatling | ✅ 可处理 | 用 constantUsersPerSec / rampUsersPerSec 的开模型 |
| k6 | ✅ 可处理 | 用 constant-arrival-rate / ramping-arrival-rate |
wrk2 的修正用法:
# 按固定 20000 req/s 发压,延迟按"本该发出的时间"计算
wrk2 -t8 -c256 -R20000 -d60s --latency http://127.0.0.1:8080/api/orders/1
k6 的开模型写法:
export const options = {
scenarios: {
peak: {
executor: 'ramping-arrival-rate',
startRate: 100,
timeUnit: '1s',
preAllocatedVUs: 200,
maxVUs: 2000,
stages: [
{ target: 1000, duration: '2m' },
{ target: 3000, duration: '3m' },
{ target: 0, duration: '1m' },
],
},
},
};
Gatling 的开模型写法:
setUp(
scn.inject(
rampUsersPerSec(100).to(3000).during(5.minutes)
)
)
6.3 四个工具的延迟口径
| 工具 | 默认延迟指标 | 包含什么 | 注意 |
|---|---|---|---|
| JMeter | elapsed / latency / connect | elapsed 是整请求;latency 是首字节 | 报告里默认给平均值,必须手动看分位数 |
| wrk | Latency | 单请求往返 | --latency 才有分位数 |
| Gatling | response time | 请求开始到响应结束,不含 pause | 报告默认给分位数,比较良心 |
| k6 | http_req_duration | sending + waiting + receiving | http_req_waiting 才是 TTFB |
6.4 结论
报告完整度:Gatling > JMeter > k6 > wrk
结果可信度:取决于你有没有用开模型,而不是取决于工具。 用闭模型压一个会变慢的系统,四个工具都会给你一个偏乐观的 P99。
七、维度五:CI/CD 与云原生
7.1 门禁能力
| 工具 | 失败退出码 | 官方 CI 集成 | 典型做法 |
|---|---|---|---|
| JMeter | ⚠ 默认不反映断言失败 | jmeter-maven-plugin | 解析 .jtl 或插件 ignoreResultFailures=false |
| wrk | ❌ 没有失败概念 | 无 | 自己 grep 输出,脆弱 |
| Gatling | ✅ 断言失败构建失败 | Maven / Gradle / sbt 插件 | mvn gatling:test |
| k6 | ✅ 阈值失败退出码 99 | CLI 即可,无需插件 | k6 run script.js |
k6 的门禁写法最直接:
export const options = {
thresholds: {
http_req_duration: ['p(95)<300', 'p(99)<800'],
http_req_failed: ['rate<0.005'],
},
};
任何一条不满足,进程退出码 99,流水线直接红。
Gatling 的断言同理:
.assertions(
global.responseTime.percentile3.lt(500),
global.successfulRequests.percent.gt(99.5)
)
7.2 容器化
| 工具 | 镜像形态 | 大致体积 | 启动速度 |
|---|---|---|---|
| JMeter | JVM + JMeter | 400–600MB | 数秒 |
| wrk | Alpine + 二进制 | <20MB | 毫秒级 |
| Gatling | JVM + Gatling | 500MB+ | 数秒 |
| k6 | 单二进制 | ~80MB | 毫秒级 |
在 Kubernetes 里跑一次性压测 Job,镜像大小直接影响调度和冷启动。wrk 和 k6 在这一项上优势明显。
7.3 分布式压测
| 工具 | 方案 | 成本 |
|---|---|---|
| JMeter | 自带 controller / worker | 免费,但要做 RMI 网络与时间同步 |
| wrk | 无 | 自己起多个进程/机器再汇总 |
| Gatling | Gatling Enterprise | 商业授权 |
| k6 | k6 Operator(K8s)/ Grafana Cloud k6 | Operator 免费,Cloud 按量 |
JMeter 的分布式是四家里唯一"开源且原生"的,但配置并不轻松:worker 要开 jmeter-server,controller 要能访问 worker 的 RMI 端口,结果汇总还有时钟偏差问题。
# jmeter.properties(controller 侧)
remote_hosts=10.0.0.11:1099,10.0.0.12:1099
# worker 侧
jmeter-server -Djava.rmi.server.hostname=10.0.0.11
# controller 侧,-r 表示分发到 remote_hosts
jmeter -n -t plan.jmx -r -l result.jtl -e -o report/
7.4 与可观测性栈集成
| 工具 | Prometheus | Grafana | InfluxDB | 备注 |
|---|---|---|---|---|
| JMeter | ⚠ 插件 | ⚠ 通过 Backend Listener | ✅ | 配置繁琐 |
| wrk | ❌ | ❌ | ❌ | 只能解析文本 |
| Gatling | ⚠ 插件 | ⚠ | ⚠ | 开源版偏弱 |
| k6 | ✅ remote-write | ✅ 原生生态 | ✅ | 一条参数搞定 |
k6 run --out experimental-prometheus-rw script.js
7.5 许可证
| 工具 | 许可证 | 商用注意 |
|---|---|---|
| JMeter | Apache-2.0 | 无限制 |
| wrk | Apache-2.0 | 无限制 |
| Gatling | Apache-2.0(开源核心) | 分布式/企业功能需商业授权 |
| k6 | AGPL-3.0 | 内部使用没问题;嵌入对外产品需法务评估 |
k6 的 AGPL 是最容易被忽略的一条。只是内部压测、CI 跑脚本,不构成分发;但如果把 k6 打包进对外销售的软件,就要认真看一遍许可证。
7.6 结论
CI 友好度:k6 > Gatling > JMeter > wrk
云原生程度:k6 > Gatling > JMeter > wrk
八、维度六:生态与长期维护
8.1 插件与扩展
| 工具 | 扩展方式 | 生态规模 | 典型扩展 |
|---|---|---|---|
| JMeter | 插件包 + JSR223 | 最大 | Custom Thread Groups、PerfMon、WebSocket、gRPC |
| wrk | Lua 脚本 | 很小 | 自定义请求、简单断言 |
| Gatling | Scala/Java 库 + 官方模块 | 中等 | JMS、gRPC、JDBC feeder |
| k6 | xk6 扩展 + JS 库 | 中等且增长快 | SQL、Redis、Kafka、MQTT、浏览器 |
JMeter 的插件生态是它最深的护城河。很多"JMeter 不支持"的协议,最后都能找到一个社区插件——代价是插件质量参差不齐,版本兼容性要自己扛。
8.2 版本活跃度
| 工具 | 维护状态 | 风险 |
|---|---|---|
| JMeter | Apache 基金会,5.6.x 持续维护 | 迭代慢,但不会消失 |
| wrk | 4.2.0 后基本冻结 | 新特性别指望,能用多久看社区 |
| Gatling | 商业公司维护,3.x 活跃 | 开源版功能有边界,高级能力导向商业版 |
| k6 | Grafana 主导,版本活跃 | 与 Grafana 生态绑定加深 |
wrk 的"停更"不一定是问题——它足够小、足够稳定,HTTP/1.1 的压测需求也不会变。但如果你的目标是 HTTP/2、gRPC,它连门槛都进不去。
8.3 团队适配
| 团队画像 | 最顺手 |
|---|---|
| 测试团队为主,开发参与少 | JMeter |
| 后端团队,习惯写代码 | k6 / Gatling |
| 有 Scala/JVM 背景 | Gatling |
| 前端也能帮忙改脚本 | k6 |
| 只做接口冒烟 | wrk |
8.4 结论
生态广度:JMeter > k6 > Gatling > wrk
长期活跃度:k6 ≈ Gatling > JMeter > wrk
九、综合对比
9.1 六维评分
| 维度 | JMeter | wrk | Gatling | k6 |
|---|---|---|---|---|
| 上手成本 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
| 脚本表达力 | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 协议/场景覆盖 | ⭐⭐⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 压测端性能 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 报告与指标 | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| CI / 云原生 | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 综合 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
9.2 关键数据汇总
| 指标 | JMeter | wrk | Gatling | k6 |
|---|---|---|---|---|
| 压测端吞吐(256 并发) | 2.5–5 万 | 40–55 万 | 18–30 万 | 8–15 万 |
| HTTP/2 | 插件 | ❌ | 视版本 | ✅ |
| 开模型压测 | 插件 | wrk2 | ✅ | ✅ |
| 断言门禁 | 需解析 | ❌ | ✅ | ✅ |
| 分布式 | 自带 | ❌ | 商业版 | Operator |
| 单二进制 | ❌ | ✅ | ❌ | ✅ |
| 许可证 | Apache-2.0 | Apache-2.0 | Apache-2.0 | AGPL-3.0 |
9.3 适用场景
| 场景 | 推荐 | 原因 |
|---|---|---|
| 接口快速验证 | wrk | 一条命令,10 秒出结果 |
| 网关/Nginx 调优 | wrk | 压测端开销最低,不干扰判断 |
| 复杂业务链路 | JMeter | 元件组合 + 插件,非程序员也能维护 |
| JDBC/JMS/老协议 | JMeter | 四家里唯一覆盖 |
| 漂亮报告 + 复杂场景 | Gatling | DSL + 报告 + 断言 |
| CI 门禁 | k6 | 阈值退出码 + 单二进制 |
| K8s 分布式压测 | k6 | Operator 原生 |
| HTTP/2、gRPC | k6 | 原生支持 |
| 极限吞吐基准 | wrk | 压测端天花板最高 |
十、选型建议:按场景给答案
10.1 决策树
你要压什么?
├── 只想快速知道"这个接口能扛多少"(HTTP/1.1)
│ └── wrk(要看真实延迟再加 wrk2 -R)
├── 复杂业务链路:登录、token、多接口关联、多协议
│ └── JMeter
├── 每次合入都要跑、结果要卡门禁、要接 Grafana
│ └── k6
├── 团队有 JVM/Scala 背景、要漂亮报告、要长期维护
│ └── Gatling
└── 要压 HTTP/2 / gRPC / WebSocket
└── k6(或 JMeter + 插件)
10.2 如果只能选一个
选 k6。
理由不是它每一项都最强,而是它在"够用"和"能进流程"之间平衡得最好:
- 单机 8–15 万 RPS,覆盖绝大多数业务的压测需求
- 脚本是代码,能进 Git、能 Review、能复用
- 阈值即门禁,CI 里一行命令就能卡住性能回退
- HTTP/2、gRPC、WebSocket 都是原生能力
- 输出能直接接 Prometheus + Grafana,和现有可观测性栈打通
wrk 留着做接口基线——一个二进制,不占地方,排查网关问题时比什么都快。
JMeter 只在两种情况下保留:需要压 JDBC/JMS 等非 HTTP 协议,或者测试团队需要不写代码维护脚本。
Gatling 适合已经深度使用 JVM 技术栈、且对报告和场景编排有强要求的团队。
10.3 三种典型组合
| 团队阶段 | 组合 | 分工 |
|---|---|---|
| 起步期 | wrk + k6 | wrk 做接口基线,k6 做 CI 冒烟 |
| 成长期 | k6 + JMeter | k6 做门禁和日常压测,JMeter 补复杂协议 |
| 成熟期 | k6 + Gatling + wrk | k6 门禁、Gatling 全链路、wrk 极限基准 |
工具不是婚姻,没必要从一而终。关键是统一指标口径,否则三个工具测出三套数字,复盘时又要吵架。
10.4 反模式清单
| 反模式 | 后果 | 正确做法 |
|---|---|---|
| JMeter GUI 模式跑正式压测 | GUI 和监听器吃掉大量资源,数字失真 | jmeter -n,关闭结果树 |
| 只看平均响应时间 | 长尾被平均掉,线上照样超时 | 看 P99/P999 |
| 恒定并发一把压到底 | 看不到拐点,也无法定位容量 | 阶梯加压 + 预热 |
| 压测机和目标机同机 | 互相抢 CPU,测的是自己 | 分机部署,先做压测端自检 |
| 所有请求同一个 ID | 缓存全命中,数字虚高 | 参数化 + 真实数据分布 |
| 用 wrk 做 CI 门禁 | 没有断言、没有退出码 | 用 k6 或 Gatling |
| 用 k6 压工具极限 | 先被 JS 运行时拖住,误判目标容量 | 极限吞吐用 wrk |
| 拿一次压测结果当永久容量 | 版本、数据量、依赖都在变 | 每次发布前回归 |
10.5 从 JMeter 迁到 k6 的映射表
| JMeter | k6 对应 |
|---|---|
| Thread Group | scenarios |
| Ramp-up | ramping-vus |
| Loop Count | iterations |
| CSV Data Set Config | SharedArray + open() |
| JSON Extractor | res.json('$.field') |
| Response Assertion | check() |
| Constant Throughput Timer | constant-arrival-rate |
| Backend Listener | --out experimental-prometheus-rw |
| Aggregate Report | handleSummary() |
迁移的难点不在语法,在思维:JMeter 是"配置元件",k6 是"写代码"。前者靠点,后者靠想。
十一、比工具更重要的事:一次可信压测的检查清单
11.1 压测机不能是瓶颈
# 压测端极限自检
wrk -t8 -c256 -d30s --latency http://127.0.0.1:9999/ping
# 观察
mpstat -P ALL 1 # CPU 是否单核打满
sar -n DEV 1 # 网卡是否打满
ss -s # 连接数
ulimit -n # 文件描述符
压测端 CPU 超过 80%、网卡接近带宽上限、出现大量 TIME_WAIT,任何一条成立,这轮数据就只能参考,不能作为容量结论。
11.2 阶梯加压,不要一把梭
并发
3000 ┤ ╭────╮
2000 ┤ ╭─────╯ ╰───
1000 ┤ ╭─────╯
0 ┼────╮───╯
└────┴────┴────┴────┴────┴────▶ 时间
预热 爬坡 稳态 峰值 回落
爬坡段用来找拐点,稳态段用来测容量,峰值段用来验证过载保护。只有稳态段的数据,说明不了系统在压力变化时的行为。
11.3 看分位数、错误率和饱和度
| 指标 | 看什么 | 危险信号 |
|---|---|---|
| P99 | 长尾用户体验 | 超过 SLA 的 1.5 倍 |
| P999 | 极端慢请求 | 出现秒级尖刺 |
| 错误率 | 非 2xx / 超时 / 断言失败 | 拐点前就开始上升 |
| CPU | 目标机使用率 | 持续 >80% |
| GC | Full GC 频率与停顿 | 吞吐上升时 GC 时间同步上升 |
| 连接池 | 等待队列长度 | 请求排队,QPS 不再增长 |
| 线程池 | 活跃线程与队列 | 队列持续增长 |
11.4 数据和缓存要"像生产"
压测数字虚高,最常见的原因是数据不真实:
- 所有请求同一个用户、同一个商品 → 缓存命中率接近 100%
- 数据库只有 1 万行 → 索引全部在内存
- 没有走真实的下游依赖 → 跳过了最慢的那一跳
至少准备三组数据:热数据(缓存命中)、冷数据(穿透)、混合分布(接近生产)。
11.5 上线前检查清单
| 检查项 | 标准 |
|---|---|
| 压测端自检 | 目标 QPS < 压测端极限的 60% |
| 压测模式 | 非 GUI(JMeter)/ 独立进程(wrk) |
| 负载模型 | 阶梯加压,含预热与稳态 |
| 指标口径 | P99 + 错误率 + 饱和度 |
| 数据分布 | 热/冷/混合三组 |
| 依赖 | 下游不 mock,或明确标注 mock 范围 |
| 结果留存 | 原始样本 + 报告 + 脚本版本号 |
| 回归对比 | 与上一版本同口径对比 |
十二、常见问题
12.1 wrk 支持 HTTPS 和 HTTP/2 吗?
HTTPS 支持,但编译时要带 OpenSSL:
make WITH_OPENSSL=1
HTTP/2 不支持,wrk2 也不支持。要压 HTTP/2,用 k6、Gatling(确认版本)或 h2load。
12.2 JMeter 的 GUI 到底能不能压测?
能跑,但不该跑。GUI 里的监听器会把每个请求的完整结果留在内存里,几千并发就能把堆打爆;GUI 自身的渲染也抢 CPU。正确姿势是 GUI 只用来编脚本和调试,正式压测一律 jmeter -n,并且关闭"查看结果树"。
12.3 k6 比 wrk 慢,是不是不适合大压力?
看你的"大压力"是多少。k6 单机 8–15 万 RPS,对绝大多数业务服务来说远超其承载能力。真正需要压到几十万、上百万 RPS 的场景(网关、静态资源、极限基准),才应该用 wrk。k6 的短板是压测端开销,不是场景能力。
12.4 Gatling 必须会 Scala 吗?
3.7 之后有 Java DSL,Java 团队可以不写 Scala。但复杂场景、自定义 feeder、动态断言,最后还是绕不开一点 Scala 知识。如果团队完全没有 JVM 函数式编程经验,k6 的上手成本更低。
12.5 为什么压测结果和线上差那么多?
通常是四件事没对齐:流量模型(并发 vs 到达率)、数据分布(缓存命中率)、依赖范围(mock 掉了最慢的下游)、环境差异(机器规格、网络延迟、GC 参数)。压测不是"跑个数字",是"复现生产流量模型"。
12.6 四个工具能混用吗?
能,而且推荐。常见组合是:wrk 做接口基线和网关调优,k6 做 CI 门禁,JMeter 补复杂协议,Gatling 做全链路回归。前提是统一指标口径——同一套 SLA、同一种延迟定义、同一套数据分布,否则数字没有可比性。
12.7 k6 的 AGPL 许可证有风险吗?
内部使用、CI 跑脚本、自建 Prometheus 输出,都不涉及分发,没有额外义务。只有把 k6 嵌入对外交付的产品时才需要评估。拿不准就让法务看一眼,不要靠猜。
12.8 压测机要几台?
先测单台极限,再按"目标 QPS ÷ 单台极限 × 1.7"估算。系数 1.7 是留给网络抖动、脚本复杂度和安全余量的。能一台搞定就别上分布式——分布式压测的时间同步和结果汇总本身就会引入误差。
十三、总结
最终结论
压测工具选型:
├── 快速验证 / 接口基线 / 网关调优 → wrk
├── 复杂业务链路 / 多协议 / 非程序员维护 → JMeter
├── 复杂场景 + 漂亮报告 + JVM 团队 → Gatling
├── CI 门禁 / 云原生 / HTTP/2 / gRPC → k6
└── 只能选一个 → k6(wrk 作为补充)
六维速查表
| 维度 | JMeter | wrk | Gatling | k6 |
|---|---|---|---|---|
| 上手成本 | 中 | 最低 | 最高 | 低 |
| 表达力 | 中 | 低 | 最高 | 高 |
| 协议覆盖 | 最广 | 仅 HTTP/1.1 | 较广 | 较广 |
| 压测端性能 | 最低 | 最高 | 高 | 中 |
| 报告 | 完整 | 无 | 最漂亮 | 够用 |
| CI/云原生 | 弱 | 弱 | 强 | 最强 |
一句话
wrk 回答"这台机器能发多快",JMeter 回答"这条业务链路能不能跑通",Gatling 回答"复杂场景下的性能画像是什么",k6 回答"这次合入有没有让系统变慢"——工具本身没有高下,问题没有问对,才是真的白压。
关键数据
- 同样 256 并发,压测端吞吐:wrk 40–55 万 > Gatling 18–30 万 > k6 8–15 万 > JMeter 2.5–5 万 RPS
- wrk 只支持 HTTP/1.1;k6 原生支持 HTTP/2、gRPC、WebSocket
- JMeter 默认退出码不反映断言失败;k6 阈值失败退出码 99
- 闭模型压测会低估 P99,wrk2、Gatling 开模型、k6 arrival-rate 都能修正
- 目标 QPS 不应超过压测端极限的 50–60%
给团队的建议
| 项目状态 | 建议 |
|---|---|
| 还没有压测体系 | 先上 k6,配 CI 冒烟,再补 wrk 基线 |
| 已有大量 JMeter 脚本 | 保留复杂链路,简单接口逐步迁到 k6 |
| 测试团队主导 | JMeter 继续用,但强制 CLI 模式 |
| 追求极限吞吐 | wrk + wrk2,别用业务工具压网关 |
| 要接 Grafana | k6 + Prometheus remote-write |
| 要压 HTTP/2 / gRPC | k6,别在 JMeter 插件上耗时间 |
| 准备上生产 | 按 11.5 的检查表逐项过,先做压测端自检 |
互动话题:你们团队现在用哪个压测工具?有没有遇到过"压测报告很漂亮、上线就崩"的情况?是工具选错了,还是压测方法本身有问题?评论区聊聊,点赞最高的场景我拆成下一篇的完整复盘。
参考资料
- Apache JMeter 官方文档
- JMeter Best Practices: 不要用 GUI 模式压测
- wrk GitHub 仓库
- wrk2 GitHub 仓库(含 coordinated omission 说明)
- Gatling 官方文档
- Gatling: Injection 与开模型/闭模型
- Grafana k6 官方文档
- k6: Thresholds 与退出码
- k6: Scenarios 与执行器
- Gil Tene: How NOT to Measure Latency
- 《消息队列三巨头:Kafka vs RocketMQ vs Pulsar——百万级消息吞吐实测》
- 《Kafka 为什么这么快?——顺序写 + 页缓存 + 零拷贝 + 批量,四板斧拆解》
- 《本地缓存三巨头:Caffeine vs Guava Cache vs Ehcache——到底谁才是王者》
- 《JSON 序列化框架性能横评:Jackson vs Gson vs Fastjson2 vs Moshi——不只是快慢》
- 《MySQL vs PostgreSQL 性能横评:10 个场景的真实数据》
- 《服务间通信三巨头:Feign vs Dubbo vs gRPC——10000 并发实测》
标题:性能压测工具横评:JMeter vs wrk vs Gatling vs k6——该用哪个
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/10/26/1792923134743.html
公众号:服务端技术精选
- 引言
- 一、四个工具,四种出身
- 1.1 JMeter:功能最全,也最"重"
- 1.2 wrk:一把只做一件事的手术刀
- 1.3 Gatling:把压测写成代码
- 1.4 k6:开发者体验优先
- 二、评测方法:先把"公平"定义清楚
- 2.1 测试环境
- 2.2 统一场景
- 2.3 同一个场景,四种写法
- 2.4 度量口径
- 三、维度一:上手成本与表达力
- 3.1 第一条压测命令
- 3.2 复杂场景的表达力
- 3.3 可维护性
- 3.4 结论
- 四、维度二:协议覆盖与场景编排
- 4.1 协议矩阵
- 4.2 场景编排矩阵
- 4.3 一个容易被忽略的差异:开模型 vs 闭模型
- 4.4 结论
- 五、维度三:压测端性能与资源开销
- 5.1 先搞清楚:你测的可能是压测机
- 5.2 压测端吞吐实测
- 5.3 资源占用
- 5.4 压测机自检:别让工具背锅
- 5.5 结论
- 六、维度四:结果可信度与报告
- 6.1 报告能力
- 6.2 协调遗漏:一个把 P99 从 2 秒变成 200 毫秒的陷阱
- 6.3 四个工具的延迟口径
- 6.4 结论
- 七、维度五:CI/CD 与云原生
- 7.1 门禁能力
- 7.2 容器化
- 7.3 分布式压测
- 7.4 与可观测性栈集成
- 7.5 许可证
- 7.6 结论
- 八、维度六:生态与长期维护
- 8.1 插件与扩展
- 8.2 版本活跃度
- 8.3 团队适配
- 8.4 结论
- 九、综合对比
- 9.1 六维评分
- 9.2 关键数据汇总
- 9.3 适用场景
- 十、选型建议:按场景给答案
- 10.1 决策树
- 10.2 如果只能选一个
- 10.3 三种典型组合
- 10.4 反模式清单
- 10.5 从 JMeter 迁到 k6 的映射表
- 十一、比工具更重要的事:一次可信压测的检查清单
- 11.1 压测机不能是瓶颈
- 11.2 阶梯加压,不要一把梭
- 11.3 看分位数、错误率和饱和度
- 11.4 数据和缓存要"像生产"
- 11.5 上线前检查清单
- 十二、常见问题
- 12.1 wrk 支持 HTTPS 和 HTTP/2 吗?
- 12.2 JMeter 的 GUI 到底能不能压测?
- 12.3 k6 比 wrk 慢,是不是不适合大压力?
- 12.4 Gatling 必须会 Scala 吗?
- 12.5 为什么压测结果和线上差那么多?
- 12.6 四个工具能混用吗?
- 12.7 k6 的 AGPL 许可证有风险吗?
- 12.8 压测机要几台?
- 十三、总结
- 最终结论
- 六维速查表
- 一句话
- 关键数据
- 给团队的建议
- 参考资料
评论