1. 简介
选择合适的性能测试工具并不容易。本文将通过一个简单的 REST API 测试案例,对三款主流的 Web 应用负载测试工具进行横向对比:Apache JMeter、Gatling 和 The Grinder。
2. 工具概览
先快速了解一下这三款工具的基本背景。
2.1. Gatling
Gatling 是一款基于 Scala 编写的高性能负载测试工具。其最大的亮点之一是提供了 Recorder 功能,可以自动生成 Scala 脚本,极大提升了脚本开发效率。如果你熟悉 Scala,使用 Gatling 会如鱼得水。
更多内容可参考我们的 Gatling 入门指南。
2.2. JMeter
JMeter 是 Apache 基金会旗下的老牌性能测试工具,拥有非常友好的图形界面(GUI),适合非编程人员快速上手。它的 逻辑控制器(Logic Controllers) 功能非常强大,可以灵活地组织测试逻辑。
更多内容可参考我们的 JMeter 入门指南。
2.3. The Grinder
The Grinder 是基于 Jython 的脚本化测试工具,相比前两者更具编程性。它支持 Console 和 Agent 架构,可以轻松实现跨服务器的分布式压测,非常适合大规模测试场景。
它也被定位为“专为开发者设计的负载测试工具”,用于发现死锁和性能瓶颈。
3. 测试场景设计
我们准备了一个简单的 REST API 用于测试,功能包括:
- 添加/更新积分账户
- 查询单个或所有积分账户
- 绑定交易到积分账户
- 查询积分账户下的所有交易
3.1. 我们的 REST API
API 接口如下:
@PostMapping(path="/rewards/add")
public @ResponseBody RewardsAccount addRewardsAcount(@RequestBody RewardsAccount body)
@GetMapping(path="/rewards/find/{customerId}")
public @ResponseBody Optional<RewardsAccount> findCustomer(@PathVariable Integer customerId)
@PostMapping(path="/transactions/add")
public @ResponseBody Transaction addTransaction(@RequestBody Transaction transaction)
@GetMapping(path="/transactions/findAll/{rewardId}")
public @ResponseBody Iterable<Transaction> findTransactions(@PathVariable Integer rewardId)
这些接口之间的关系(如通过 customerId
查询 rewardId
)在测试脚本中需要手动处理。
3.2. 自动化测试流程
为了公平对比,我们为三款工具使用相同的测试逻辑:
- 随机生成客户 ID
- 发起一笔交易
- 解析响应获取客户 ID 和交易 ID
- 查询客户积分账户 ID
- 解析响应获取积分账户 ID
- 如果积分账户不存在,则创建
- 更新交易,绑定积分账户 ID
- 查询该积分账户下的所有交易
3.3. Gatling 脚本示例
Gatling 的 DSL 风格非常简洁,下面是第 4 步的示例:
.exec(http("get_reward")
.get("/rewards/find/${custId}")
.check(jsonPath("$.id").saveAs("rwdId")))
Gatling 支持 JSON Path 解析响应内容,非常方便。
请求体中使用表达式语言处理动态字段:
.body(StringBody(
"""{
"customerRewardsId":"${rwdId}",
"customerId":"${custId}",
"transactionDate":"${txtDate}"
}""")).asJson)
测试配置如下:
val scn = scenario("RewardsScenario")
.repeat(1000) {
...
}
setUp(
scn.inject(atOnceUsers(100))
).protocols(httpProtocol)
完整脚本见 GitHub。
3.4. JMeter 脚本示例
JMeter 通过 GUI 配置后会生成 .jmx
文件,内容如下:
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Add Transaction" enabled="true">
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="Transaction Id Extractor" enabled="true">
线程配置:
<stringProp name="ThreadGroup.num_threads">100</stringProp>
完整 .jmx
文件见 GitHub。
3.5. The Grinder 脚本示例
The Grinder 使用 Jython 脚本,风格简洁但需要手动处理 JSON 解析:
customerId = str(random.nextInt());
result = request1.POST("http://localhost:8080/transactions/add",
"{"'"customerRewardsId"'":null,"'"customerId"'":"+ customerId + ","'"transactionDate"'":null}")
txnId = parseJsonString(result.getText(), "id")
配置通过 grinder.properties
文件:
grinder.threads = 100
grinder.processes = 1
grinder.runs = 1000
完整脚本见 GitHub。
4. 执行与结果
4.1. 执行方式
三款工具都推荐使用命令行执行大规模测试。
- Gatling 3.4.0(开源版)
- JMeter 5.3
- The Grinder 3
Gatling 启动命令:
./gatling.sh
JMeter 启动命令:
./jmeter.sh -n -t TestPlan.jmx -l log.jtl
The Grinder 启动命令:
java -classpath $CLASSPATH net.grinder.Console
java -classpath $CLASSPATH net.grinder.Grinder $GRINDERPROPERTIES
4.2. 测试结果对比
工具 | 成功请求数 | 错误数 | 总耗时(s) | 平均响应时间(ms) | 吞吐量(req/s) |
---|---|---|---|---|---|
Gatling | 500000 | 0 | 218 | 42 | 2283 |
JMeter | 499997 | 0 | 237 | 46 | 2101 |
The Grinder | 499997 | 0 | 221 | 43 | 2280 |
✅ 性能方面,三者差距不大,Gatling 略胜一筹。
4.3. 报告展示
Gatling 报告
自动生成 HTML 报告,图表丰富直观:
JMeter 报告
支持 GUI 打开 .jtl
日志生成 HTML 报告:
The Grinder 报告
控制台界面记录各 Agent 的统计信息:
5. 工具综合评分
项目 | Gatling | JMeter | The Grinder |
---|---|---|---|
项目活跃度 | 9 | 9 | 6 |
性能表现 | 9 | 8 | 9 |
脚本能力/API | 7 | 9 | 8 |
用户界面 | 9 | 8 | 6 |
报告质量 | 9 | 7 | 6 |
集成能力 | 7 | 9 | 7 |
总分 | 8.3 | 8.3 | 7 |
总结建议:
- ✅ Gatling:适合需要美观报告和高交互性的团队,Scala 用户友好。
- ✅ JMeter:适合复杂业务逻辑、多种协议支持的测试场景,社区成熟。
- ✅ The Grinder:适合需要高性能和跨服务器扩展的开发者。
6. 结论
三款工具各有千秋,选择时应根据团队技术栈、测试复杂度和报告需求综合考虑。没有银弹,只有最适合的工具。