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. 自动化测试流程

为了公平对比,我们为三款工具使用相同的测试逻辑:

  1. 随机生成客户 ID
  2. 发起一笔交易
  3. 解析响应获取客户 ID 和交易 ID
  4. 查询客户积分账户 ID
  5. 解析响应获取积分账户 ID
  6. 如果积分账户不存在,则创建
  7. 更新交易,绑定积分账户 ID
  8. 查询该积分账户下的所有交易

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 报告,图表丰富直观:

gatling result new 1

JMeter 报告

支持 GUI 打开 .jtl 日志生成 HTML 报告:

2020/10/jmeter-result-new.png

The Grinder 报告

控制台界面记录各 Agent 的统计信息:

grinder result new 1

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. 结论

三款工具各有千秋,选择时应根据团队技术栈、测试复杂度和报告需求综合考虑。没有银弹,只有最适合的工具。


原始标题:Gatling vs JMeter vs The Grinder | Baeldung