转自:https://yq.aliyun.com/articles/708146?spm=a2c4e.11155472.0.0.4ad91c70YfClbo

云时代 JMeter 最佳实践

 

hyerty 2019-07-08 18:12:57 浏览1489

监控

日志

线程

阿里技术协会

配置

测试

脚本

性能测试

压力测试

csv

PTS

JMeter

 

Apache JMeter 是 Apache 旗下的开源压测工具,创建于 1999 年初,迄今已有超过 20 年历史。JMeter 功能丰富,社区(用户群体)庞大,是主流开源压测工具之一。

性能测试通常集中在新系统上线或大型活动前(如电商大促,春节活动等),以验证系统能力,帮助排查定位性能瓶颈等问题。

一次压测活动可粗略分为几个步骤:

  1. 场景配置。配置压测场景模拟用户(业务)与系统的交互。
  2. 压测执行。按指定压力量级启动压测。
  3. 压测监控分析。压测中通常关注施压 RPS,成功率,业务响应时间(RT),网络带宽等关键指标。
  4. 报告总结。披露系统能力是否符合要求,同时沉淀记录系统性能演变和优化过程。

下面我们讨论如何使用 JMeter 完成上述步骤,及相关的最佳实践建议。

JMeter 使用 Java 开发,需要先安装 JDK 并配置好 PATH 环境变量,然后从官网下载 JMeter 二进制压缩包解压即可。建议将 JMeter bin 目录也添加到 PATH 环境变量,这样在命令行下输入 jmeter命令即可启动 JMeter 。

场景配置

简单 HTTP 请求配置

最常见的压测场景即 HTTP 压测。压测场景在 JMeter 脚本中叫做 Test Plan(压测计划),打开 JMeter 默认即为一个空 Test Plan 。JMeter 使用并发(线程)数控制压力大小,一个线程可看做一个执行请求的虚拟用户。在 Test Plan 上点击右键,添加一个 Thread Group(线程组)。

jemeter接口最大压力测试 jmeter单接口压测_jemeter接口最大压力测试

线程组默认为 1 个线程并只执行一次 1 次,这很方便测试执行脚本,保持此默认值即可。

jemeter接口最大压力测试 jmeter单接口压测_HTTP_02

JMeter 中发送请求的组件叫做 Sampler(采样器)。在 Thread Group 上单击右键,添加一个 HTTP Request 节点(采样器)。

jemeter接口最大压力测试 jmeter单接口压测_CSV_03

HTTP 请求最关键的配置即 URL,JMeter 允许将 URL 协议类型(Protocol)、服务器名、请求路径(Path)等拆开单独配置。也可以直接将整个 URL(如 JMeter 主页 http://jmeter.apache.org/ )填写到 Path,其他字段保留为空即可。这样,一个最简单的 HTTP 压测脚本就配置好了。

jemeter接口最大压力测试 jmeter单接口压测_jemeter接口最大压力测试_04

为了方便测试、调试脚本,可在 Test Plan 下添加一个 View Results Tree 监听器(Listener)。这个监听器仅用于编辑脚本时测试、调试脚本,查看请求执行详情,不需要做任何配置。

jemeter接口最大压力测试 jmeter单接口压测_压测_05

测试执行脚本

第一次执行脚本前,需要先保存脚本,如保存为 test.jmx 。以后每次执行脚本前,JMeter 默认会自动保存脚本。

连续多次执行脚本时,JMeter 默认不会清理历史记录。为了避免历史执行结果干扰,可先点击 Clear All 按钮手动清空历史记录,再点击 Start 按钮执行脚本,这样看到的执行结果更清爽,方便排查问题。

jemeter接口最大压力测试 jmeter单接口压测_CSV_06

按照默认线程组配置,脚本执行一次即结束。点击 View Results Tree ,可看到请求执行详细信息,包括请求头,请求体,响应头和完整的响应体等信息。

jemeter接口最大压力测试 jmeter单接口压测_HTTP_07

场景编排

真实压测场景通常不会只有一个请求,而是多个请求按一定顺序和规则的编排组合,即场景编排。场景编排是 JMeter 等压测引擎最重要的功能之一,也是与 apache ab 等简单压测工具的重要区别之一。

这里我们假设一个最简单的场景,先访问 JMeter 主页,停留 1 秒钟后跳转到下载页。

一个脚本访问一个网站的不同页面(Path)时,可添加一个 HTTP Request Defaults 节点,配置默认协议类型和服务器名。这样可避免重复配置,需要修改协议类型(如 https 与 http 切换)或压测域名时,只用修改 HTTP Request Defaults 即可。

jemeter接口最大压力测试 jmeter单接口压测_HTTP_08

HTTP Request Defaults 配置服务器名为 jmeter.apache.org(协议类型默认为 http),鼠标可拖动 HTTP Request Defaults 节点移动到 HTTP 请求节点之前。

jemeter接口最大压力测试 jmeter单接口压测_压测_09

每个请求节点可设置一个具有业务含义的名字,方便理解和管理。访问 JMeter 主页的 HTTP 请求可改名为 home ,同时 Path 修改为 / 。再添加一个 HTTP 请求节点,命名为 download page ,设置 Path 为 /download_jmeter.cgi 即可。

jemeter接口最大压力测试 jmeter单接口压测_jemeter接口最大压力测试_10

模拟在 home 页面停顿 1 秒钟。home 节点上右键,添加一个 Constant Timer 子节点,设置延迟时间为 1000 毫秒即可。

jemeter接口最大压力测试 jmeter单接口压测_jemeter接口最大压力测试_11

再次执行脚本,点击 View Results Tree 可看到两个 HTTP 请求节点的执行详情。

jemeter接口最大压力测试 jmeter单接口压测_CSV_12

注意:

  1. Timer 节点作为场景编排辅助节点,没有请求执行动作,也没有执行详情显示。
  2. 循环执行脚本时,最后一个节点 download page 执行结束后,会立即跳转到脚本开头,执行第一个节点 home 。可在 download page 上也添加一个 Timer,模拟停留一秒之后再继续后续请求。

压测执行

编辑、调试脚本时,我们通常设置为 1 个线程并且只执行 1 次。执行压力测试时,通常需要以较高的压力持续执行一段时间。

脚本固定配置压力

如计划以 50 并发执行 2 分钟,可修改脚本 Thread Group 配置如下。

jemeter接口最大压力测试 jmeter单接口压测_jemeter接口最大压力测试_13

配置说明:

  1. 并发数(Number of Threads (users))设置为 50 。
  2. 循环次数(Loop Count)勾选永远执行(Forever)。
  3. 勾选 Scheduler,设置执行时长(Duration (seconds))为 120 秒。

通常我们在 JMeter 图形界面(GUI)编辑脚本,但执行压力测试时 GUI 占用额外资源可能影响施压性能,而且施压机可能没有图形界面环境(如 ssh 远程登录施压机)。因此脚本编辑完成后,通常以命令行模式执行 JMeter 压力测试。

进入 JMeter 脚本目录,执行 JMeter 压力测试的命令为:

jmeter -n -t <脚本>

如执行上述 test.jmx 脚本,命令如下:

jmeter -n -t test.jmx

输出结果如下:

Creating summariser <summary>
Created the tree successfully using test.jmx
Starting the test @ Tue Jun 25 14:38:32 CST 2019 (1561444712414)
Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445
summary +    553 in 00:00:27 =   20.3/s Avg:  1378 Min:   252 Max:  8587 Err:     0 (0.00%) Active: 50 Started: 50 Finished: 0
summary +    882 in 00:00:30 =   29.4/s Avg:   685 Min:   222 Max:  4272 Err:     0 (0.00%) Active: 50 Started: 50 Finished: 0
summary =   1435 in 00:00:57 =   25.1/s Avg:   952 Min:   222 Max:  8587 Err:     0 (0.00%)
summary +    829 in 00:00:30 =   27.5/s Avg:   815 Min:   222 Max: 21310 Err:     0 (0.00%) Active: 50 Started: 50 Finished: 0
summary =   2264 in 00:01:27 =   25.9/s Avg:   902 Min:   222 Max: 21310 Err:     0 (0.00%)
summary +    881 in 00:00:30 =   29.5/s Avg:   700 Min:   221 Max:  3896 Err:     0 (0.00%) Active: 50 Started: 50 Finished: 0
summary =   3145 in 00:01:57 =   26.8/s Avg:   845 Min:   221 Max: 21310 Err:     0 (0.00%)
summary +    127 in 00:00:05 =   24.2/s Avg:   797 Min:   224 Max:  3819 Err:     0 (0.00%) Active: 0 Started: 50 Finished: 50
summary =   3272 in 00:02:02 =   26.7/s Avg:   843 Min:   221 Max: 21310 Err:     0 (0.00%)
Tidying up ...    @ Tue Jun 25 14:40:35 CST 2019 (1561444835251)
... end of run

压测过程中默认每 30 秒输出一次统计数据,2 分钟后(实际为 00:02:02,比预设的 2 分钟多出少许误差)压测结束。看最后一行统计数据,平均 RPS(每秒请求数)为 26.7,平均 RT (响应时间)为 843 毫秒。

尝试核对一下统计数据。脚本包含两个请求,每个请求附加 1 秒钟等待时间,发送一个请求平均耗时为 RT 843 毫秒 + 等待 1000 毫秒。单线程理论 RPS 为 1000.0 / (843 + 1000),总共 50 个线程,全场景理论 RPS 为 1000.0 / (843 + 1000) * 50 = 27.13,与统计值 26.7 有一定误差。这是因为除了请求 RT 和等待时间,脚本执行请求之间还可能存在少量时间消耗。

命令行动态设置压力

实际工作中,常常需要以不同的压力大小反复执行压力测试,在脚本中写死压力大小(并发数)和执行时间显然很不方便。

如何动态指定压力大小呢,这里有一个技巧。JMeter 脚本支持使用 JMeter 属性进行配置,JMeter 命令行支持使用 -J 参数动态指定 JMeter 属性。把这两者结合起来,即可实现在命令行通过 -J 参数动态设置压力大小。

修改 JMeter 脚本使用 JMeter 属性配置压力大小,配置如下:

jemeter接口最大压力测试 jmeter单接口压测_CSV_14

配置说明:

  1. 并发数配置为 ${__P(load.concurrency,1)},循环次数取消勾选 Forever,配置为 ${__P(load.count,1)}。未设置对应的 JMeter 属性时,默认为 1 ,满足只执行 1 次以测试、调试脚本的需求。
  2. 执行时长配置为 ${__P(load.duration,60)},默认 1 分钟(60 秒)。

测试命令行直接执行脚本:

jmeter -n -t test.jmx

可看到统计输出如下:

summary =      2 in 00:00:03 =    0.6/s Avg:   485 Min:   408 Max:   563 Err:     0 (0.00%)

默认一个并发并且只执行一次,发出 2 个请求(脚本循环一次发出两个请求),约 3 秒后脚本停止。注意:默认执行时间是 1 分钟,同时配置了循环次数和执行时间时,有一个条件先满足脚本即停止。

为了按指定时长执行,需要将执行次数设置为 Forever 。在 JMeter 内部实现中,执行次数为 -1 即表示 Forever 。指定以 50 并发执行 2 分钟,jmeter 命令行如下:

jmeter -n -t test.jmx -Jload.concurrency=50 -Jload.duration=120 -Jload.count=-1

执行结果与前述脚本固定配置 50 并发的结果类似。

阿里云 PTS 执行 JMeter 脚本

阿里云 PTS 是一款云化性能测试工具,同时也支持原生 JMeter 压测。

打开 PTS 控制台 主页,左侧导航栏选择 创建压测 > JMeter 压测 ,新建 JMeter 压测场景。填写场景名,如 jmeter-test 。场景配置 页面点击 上传文件 按钮,上传本地测试通过的 test.jmx 脚本。

jemeter接口最大压力测试 jmeter单接口压测_CSV_15

施压配置 页面,并发数设置为 50,压测时长设置为 2 分钟。

jemeter接口最大压力测试 jmeter单接口压测_压测_16

点击 保存去压测,弹出提示框点击 确认,PTS 即开始在云端引擎执行 JMeter 脚本发起压力。

jemeter接口最大压力测试 jmeter单接口压测_压测_17

压测中页面如下:

jemeter接口最大压力测试 jmeter单接口压测_CSV_18

压测中实时展示场景(及每个请求页面)实时 RPS 和 RT 等信息。可看到场景并发数为 50,RPS 为 26,RT 为 812 毫秒,与本地压测的结果差不多。注意:因为机器配置和网络环境的差异(PTS 施压机默认为 4核 8G,BGP多线路公网),PTS 上压测结果可能与本地压测结果存在一定差异。

针对 JMeter 施压配置,再补充几点说明:

  1. PTS 上的施压配置会覆盖原脚本中的配置。原脚本无论是写死固定配置还是使用 JMeter 属性配置都没关系。
  2. 循环次数表示每个线程循环执行脚本的次数,可能与用户的直觉理解不一样,如 总请求数 = 脚本执行一次的请求数 * 循环次数 * 并发数 。
  3. 循环次数与预热时间同时配置时可能导致意外行为(如不能达到最大并发),因此 PTS 上不允许同时配置预热时间和循环次数(即只有预热时间为 0 时才允许设置循环次数)。

压测监控分析

性能测试不仅仅是简单的发起压力,对压力负载(RPS,网络带宽等)和业务表现(RT,成功率等)的监控和分析也是压测活动的重要组成部分。

JMeter 脚本中每个请求节点(Sampler)可设置一个具有业务含义的名字(如 home 和 download page ),我们可称之为业务 API 。JMeter 监控统计按业务 API 名字汇总,如两个名字相同的请求节点将汇总统计为一个业务 API 。配置脚本时需注意,不同业务 API 节点应配置为不同的名字。

业务 API 压力负载和表现

实际工作中,不同业务 API 的统计数据可能存在巨大差异(如浏览商品 RT 通常比提交订单快很多),因此 PTS 默认将各个业务 API 独立统计展示(如上述压测中页面展示的 home 和 download page)。

压测中每个时间点的数据 PTS 都在后台记录了下来,最终将形成完整直观的压测报告。点击业务 API 实时监控趋势图按钮 

jemeter接口最大压力测试 jmeter单接口压测_CSV_19

 ,即可查看对应的 RPS,成功率,响应时间,网络带宽等监控数据的变化趋势图。

jemeter接口最大压力测试 jmeter单接口压测_CSV_20

业务 API 采样日志

很多时候我们还希望看到一个具体请求执行的详细信息。如有 1% 的请求失败,需要查看完整的请求、响应内容,以排查失败原因等。JMeter 图形界面下测试脚本时,可添加 View Results Tree 查看单个请求的详细信息,但执行压力测试时,对每个请求都记录详细信息,不仅没有必要,而且非常耗费资源,影响施压性能。

阿里云 PTS 采取了一个折中的办法,施压引擎每 5 秒对每个业务 API(压测Sampler)分别采样记录一条成功和失败(如果有)的请求详细信息。在压测中或压测报告页面,点击 查看采样日志 按钮即可查询记录的请求采样信息,并支持按业务 API(压测Sampler),响应状态(是否成功),请求时间等进行搜索过滤。

jemeter接口最大压力测试 jmeter单接口压测_jemeter接口最大压力测试_21

点击 查看详情 即可看到单个请求的详细信息。目前对详细信息提供了通用和 HTTP 两种展示模板,
HTTP 展示模板可针对 HTTP 请求进行更友好的排版展示,展示内容包括请求 URL,请求方法,返回码,完整的请求头、请求体,响应头、响应体等。

jemeter接口最大压力测试 jmeter单接口压测_CSV_22

因为页面上只展示文本内容,请求体或响应体包含图片等无法识别为文本的内容时,可能显示为乱码。另外当请求体或响应体很大时,对应的内容可能被截断。

JMeter 日志

本地执行 JMeter 脚本时,默认将日志记录到 jmeter.log 文件。在 PTS 上执行 JMeter 脚本时,可通过 JMeter日志 页面实时查看 JMeter 日志,并支持根据日志级别、时间或线程名进行查询过滤。

jemeter接口最大压力测试 jmeter单接口压测_压测_23

JMeter 日志主要用于脚本执行报错时排查错误原因。一些插件可能通过 JMeter 日志输出一些重要信息,用户在 groovy 脚本等代码中也可以直接打印日志。

日志打印过于频繁时,不仅可读性极差(大量重复日志淹没重要信息),而且影响 JMeter 性能,对 PTS 采集存储 JMeter 日志造成额外开销。因此 PTS 在采集 JMeter 日志时默认进行了限流,每秒钟打印日志条数超过 10 条时部分日志可能会丢失。良好设计的 JMeter 脚本应避免大量打印重复日志。

同样,良好设计的 JMeter 脚本应避免通过标准输出(System.out)或标准错误(System.err)打印输出信息,需要输出查看的重要信息应使用 JMeter 日志输出(如 groovy 脚本中使用 log.info("") )。
PTS 上不支持查看 JMeter 脚本执行产生的标准输出和标准错误内容。

报告总结

压测结束后,PTS 将汇总监控数据形成压测报告。用户根据压测报告分析评估系统性能是否符合要求,如 RPS,成功率和 RT(响应时间)是否符合期望。并可辅助用户排查分析业务系统性能瓶颈。

PTS 压测报告页面可查询历史压测报告列表。

jemeter接口最大压力测试 jmeter单接口压测_压测_24

点击 查看报告 打开查看报告详情。压测报告在 PTS 上默认保存 30 天,可点击 报告导出 按钮,导出保存 PDF 版压测报告到本地。压测报告概要信息包括压测执行时间,RPS,RT,成功率等概要数据。场景详情包含全场景维度和业务 API 维度的监控统计信息。注意:受 JMeter 引擎本身统计功能的限制,仅全场景维度包含并发数统计。

jemeter接口最大压力测试 jmeter单接口压测_压测_25

此外,全场景维度和业务 API 维度均包含 RPS,成功率,网络带宽等统计。如 home 请求相关监控趋势图如下。

jemeter接口最大压力测试 jmeter单接口压测_压测_26

jemeter接口最大压力测试 jmeter单接口压测_jemeter接口最大压力测试_27

相比手动命令行执行 JMeter 脚本,PTS 更加 简单易用 ,提供 简单直观的监控 ,并提供 海量施压能力 。

免费 开通 PTS 服务,购买 5000 VU 以上资源包,即可使用 JMeter 压测。PTS 计费单位是 VUM ,即并发(VU)* 分钟(最低消费 100 VUM)。上述场景消费为 50 并发 * 2 分钟 = 100 VUM 。如购买 5000 VU 资源包,包含 10 万 VUM,售价 ¥278 ,上述计费可换算为 (100 / 10万) * ¥278 = ¥0.278 。

使用 CSV 参数文件

上述 JMeter 脚本仅简单请求固定 URL ,真实业务 API 通常带有请求参数。

JMeter 中可使用 CSV Data Set Config 读取 CSV 数据文件,简单实现请求参数化。CSV 文件默认首行为变量名(列名),其余行为 CSV 数据。准备一个 user.csv 文件,包含 id , name 两列,内容如下:

id,name
1,ali
2,pts
3,jmeter

注意:手工编辑 CSV 文件容易出错,推荐使用 Excel、Numbers 等软件导出,或编程使用 apache commons-csv 生成。

编辑 JMeter 脚本,右键单击 Thread Group 添加一个 CSV Data Set Config 节点。

jemeter接口最大压力测试 jmeter单接口压测_压测_28

配置如下:

jemeter接口最大压力测试 jmeter单接口压测_压测_29

其中有两个地方需要注意(其他配置保持默认即可):

  1. Filename 配置为文件名 user.csv 即可,不要包含文件路径。不同施压机上 CSV 文件路径可能不一样,只使用文件名(并在当前路径下执行脚本)以便兼容不同的施压机环境。
  2. Sharing mode 设置为 Current thread group,指定 CSV 文件只被当前线程组使用。

假设 home 请求需要设置参数,配置请求参数直接使用 ${id} ,${name} 引用对应的变量即可。

jemeter接口最大压力测试 jmeter单接口压测_HTTP_30

测试执行脚本,查看 View Results Tree ,可看到执行请求时即会带上请求参数。

jemeter接口最大压力测试 jmeter单接口压测_jemeter接口最大压力测试_31

在阿里云 PTS 上执行脚本,只需编辑 JMeter 场景,
上传修改后的 test.jmx 脚本文件和 user.csv 数据文件,点击 保存去压测 即可。

jemeter接口最大压力测试 jmeter单接口压测_HTTP_32

查看请求采样,可看到请求 URL 已添加对应的参数。

jemeter接口最大压力测试 jmeter单接口压测_CSV_33

使用 JMeter 插件和附加 jar 包

JMeter 社区提供了丰富的插件,用户还可以自由添加使用 java 库 jar 包。在 PTS 上执行脚本时,只需要将额外添加的插件和 jar 包一起上传到 PTS 上,PTS 执行 JMeter 脚本时即可自动加载这些 jar 包。详情可参考 如何进行 WebSocket 协议的压测 。

如果遇到问题,请先确认本地已测试通过,并确认额外使用的 jar 包已全部上传到 PTS 。

海量施压能力

之前的脚本中,我们在请求后添加了 1 秒钟的等待时间以演示业务场景编排。如果压测场景前后请求没有关联,从服务器的角度看只是不停的收到各种请求,与客户端是否等待无关,因此 JMeter 脚本可去掉这些等待时间,以最大压力向服务器发起极限压测。

JMeter 使用并发(线程)数控制压力大小,通常(并发较小时)线程数越多压力越大。但单台施压机的能力毕竟有限,单机线程数增加到某个阈值时请求压力即达到极限。显然,为了继续增加压力,必须使用多台施压机进行分布式压测。

分布式压测需要注意以下几点:

每台施压机需要安装、启动 JMeter,拷贝分发脚本、CSV 数据、附加 jar 包等文件。

协调分配施压并发数。通常施压机配置相同,均分施压并发数即可。

统一控制启动、停止压测,统一收集聚合监控数据。

希望 CSV 数据唯一或打散数据时,多台施压机需要拆分 CSV 数据文件 。

使用阿里云 PTS 执行 JMeter 脚本时,用户只需要设置期望的最大并发即可 ,PTS 自动透明处理了分布式压测的问题,用户不用关心是单机压测还是分布式压测。PTS 默认每台 JMeter 施压机最大分配 500 并发,场景并发数超过 500 时即自动分配多台施压机。如果希望多台施压机拆分 CSV 数据文件,只要在上传 CSV 文件后勾选 切分文件 即可。详情参考 切分 CSV 数据文件 。

jemeter接口最大压力测试 jmeter单接口压测_压测_34

单机线程数极限与 JMeter 脚本的复杂程度和资源占用(如 CPU,内存,网络带宽占用等)情况有关。占用内存过高的 JMeter 脚本在并发过高时甚至可能会因内存溢出而导致 JMeter 引擎异常退出。对有特殊需求的高级用户,PTS 允许用户手动指定施压机数量,以更精确的控制总并发数和单机并发数。只需在 施压配置 页面勾选 指定IP数,设置相应的施压机数量即可。每台施压机有一个IP地址,指定IP数即指定施压机数量。

jemeter接口最大压力测试 jmeter单接口压测_CSV_35

注意:指定IP数后,压测执行时独占了指定数量的施压机,每台施压机将按 500 并发计费(无论实际并发多少)。此功能专为有特殊需求的高级用户提供,普通用户通常不需要使用。

PTS 自研引擎

JMeter 压测主要面向熟悉 JMeter 的用户,或已有存量 JMeter 脚本的场景。

PTS 同时自研了一套压测引擎,具备以下优点:

  1. 在线编辑场景,更加简单易用。
  2. 压测中支持手动实时调速。
  3. 支持并发模式和 RPS 模式(技术专利)两种施压模式。
  4. 特有云端录制器。
  5. 支持更高量级的海量压测能力(百万级并发)。

PTS 自研引擎目前主要支持 HTTP 压测,推荐 HTTP 压测需求的用户使用。使用入门请参考 如何在一分钟内发起压测 。

更多信息请参考 性能测试 PTS 文档 。

-----------------------------------------Have a good day!---------------------------------------------------------------------------------------------------