简介
Jmeter是Apache的开源项目,基于Java开发,主要用于进行压力测试。
优点:开源免费、支持多协议、轻量级、功能强大
官网:https://jmeter.apache.org/index.html
安装
安装步骤:
- 下载:进入jmeter的官网,进入Download页面,因为是基于Java开发的,所以不分平台,下载二进制文件即可,
- 解压安装包
- 修改配置文件:这是为了处理可能出现的中文乱码问题,在bin目录下打开jmeter.properties,将sampleresult.default.encoding的值改为UTF-8
- 配置环境变量
- 启动jmeter:进入bin目录,在Windows平台下,点击 jmeter.bat文件,启动jmeter
- 修改语言为中文:点击 Options - choose language - Chinese(Simplified)
用户界面:

修改语言:
性能测试和相关指标
jmeter主要是用于做性能测试的,所以这里了解一下性能测试的相关指标
测试的分类:测试可以简单地分为功能测试和性能测试两种,还有自动化测试,不过它更多是一种测试技巧。
功能测试:测试单个接口的功能
-
单元测试:是程序员来操作的,测试程序中的单个单元
-
冒烟测试:测试程序最基本的功能是否正常
-
回归测试:修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。
性能测试:
- 基准测试:单用户测试,测试环境确定后,对业务模型中的重要业务做单独的测试,获取单用户运行时的各项性能指标。在某个时刻,通过基准测试,建立一个已知的性能基准线,当系统的软硬件环境发生变化之后再进行一次基准测试,以确定变化对性能的影响
- 负载测试:通过逐步增加系统负载,确定在满足系统的性能指标情况下,找出系统能够承受的最大负载量的测试
- 稳定性测试:在服务器稳定运行的情况下,进行长时间测试,并最终保证服务器能满足线上业务需求。
- 并发测试:在极短的时间内,发送多个请求,来验证服务器对并发的处理能力
- 压力测试:在强负载下的测试,查看系统在峰值情况下,是否功能隐患、系统是否具有良好的容错能力和可恢复能力
- 性能测试:通过特定的方式对被测试系统按照一定测试策略施加压力,获取该系统的响应时间、TPS、吞吐量、资源利用率等性能指标,来检测系统上线后能否满足用户需求的过程。
自动化测试:编写测试脚本,通过逻辑控制器和关联等功能,测试一系列接口的功能。
性能测试的相关指标:
- QPS:Queries Per Second,每秒查询率,一台服务器每秒钟能够执行的查询次数,代表了最大吞吐量
- TPS:Transactions Per Second,吞吐量,每秒事务数,一个事务指客户端发送请求并且接收到响应的过程,客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。
- 并发数:指系统同时能处理的请求数量,同时反应了系统的负载能力。这个数值可以分析机器1秒内的访问日志数量来得到
衡量网站的性能指标
- 响应时间:从开始发送请求到接收到响应所花费的时间
- 并发数:系统同时能处理的请求数量
- 并发连接数:每秒钟服务器连接的总TCP数量
- 请求数:Query Per Second,每秒多少请求
- 事务数:TPS,每秒事务数
- 并发用户数:单位时间内有多少用户
- 吞吐量:单位时间内系统能处理的请求数量
大型互联网项目架构目标
- 高性能:提供快速的访问体验
- 高可用:网站服务一直可以正常访问
- 可伸缩:通过增加硬件,来提升处理能力
- 可扩展:模块间的耦合度低,
- 安全性:网站能够承受住外部攻击
性能测试的两件事情:模拟巨大负载、生成测试报告
入门案例
在这里,通过一个入门案例来了解jmeter的基本使用。
准备工作
编写一个springboot项目,作为本次测试的目标。
pom.xml:
xml
<groupId>org.example</groupId>
<artifactId>learn_jmeter</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--springboot工程需要继承的父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<!--web开发的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
控制器:
java
@RestController
@RequestMapping("/hello")
public class HelloController {
private static final Logger LOG = LoggerFactory.getLogger(HelloController.class);
private static int i = 1;
@GetMapping("/h1")
public String s1() {
LOG.info("第 {} 次请求", i++);
return "success";
}
}
开始测试
jmeter中的两个基本概念:
- 测试计划:jmeter中所有的测试内容都要放在测试计划之下
- 线程组:一个线程就代表一个用户。
配置测试计划:
- 在测试计划内添加线程组:右击测试计划,点击 添加 - 线程(用户) - 线程组,线程组用于发起请求,配置线程组可以设定发起请求的次数。

- 线程组内添加取样器:右击线程组 - 添加 - 取样器 - HTTP请求,将之前准备好的接口的相关信息填写进去。取样器表示发起什么样的请求,在这里选择发起http请求。

- 线程组内添加查看结果树:右测试计划 - 添加 - 监听器 - 查看结果树,不需要配置查看结果树

开始测试:点击左上角菜单栏中的运行按钮,开启测试,点击查看结果树,可以看到每个请求的统计数据
保存测试计划:点击 文件 - 保存,测试计划会被保存为jmx文件,下次直接从文件中打开即可。实际上开始测试的时候,jmeter就会提醒用户保存测试计划。
springboot中接收到的请求:

总结:
- jmeter中所有的测试内容都需要放到测试计划中
- 线程组是测试计划的基本单位,线程组的一个线程代表一个用户,可以配置线程数和每个线程的循环次数,也就是用户执行几次操作
- 每一个测试计划都必须有一个取样器和监听器:取样器用于发送请求并且接收响应,监听器用于查看结果,http请求组件是一个取样器,查看结果数树组件是一个监听器
查看性能测试的相关结果
之前的测试流程,只是简单地使用jmeter发送了一个http请求,并且可以查看结果,接下来,使用jmeter来做压力测试,并且查看压力测试的相关结果指标。
第一步:配置线程组:线程数设置为10个,循环100次
第二步:添加监听器 - 聚合报告,这是最简单的监听器,可以查看性能测试的相关指标。
第三步:开始测试。
第四步:查看结果

结果分析:
- Label:采样器的名称,例如HTTP请求的Name;
- 样本:发送请求的数量
- 平均值:平均响应时间(单位:ms);默认是单个Request的平均响应时间,当使用了Transaction Controller时,也可以以Transaction为单位显示平均响应时间;
- 中位数:50%的用户响应时间小于这个值;
- 95%百分位:95%的用户响应时间小于这个值;
- 99%百分位:99%的用户响应时间小于这个值;
- 最小值:用户响应时间最小值;
- 最大值:用户响应时间最大值;
- 异常%:测试出现的错误请求数量百分比;请求的错误率 = 错误请求的数量/请求的总数;若出现错误就要看服务端的日志查找定位原因;
- 吞吐量:Throughput简称TPS,吞吐量,默认情况下表示每秒处理的请求数,也就是指服务器处理能力,TPS越高说明服务器处理能力越好;
- KB/sec:每秒从服务器端接收到的数据量;
jmeter中的核心概念
测试计划和线程组
测试计划:存放所有的测试内容,相当于一个项目的根目录
线程组:对于线程进行分类,线程组是测试计划的基本单位,线程组中的每一个线程都代表一个用户
- 线程组的属性
- 名称
- 注释
- 在取样器错误后要执行的动作:默认选择继续,还有启动下一进程循环、停止线程、停止测试
- 线程数
- Ramp-Up时间:准备时长,设置的线程数需要多长时间全部启动。如果线程数为10,准备时长为2,那么需要2秒钟启动10个线程,也就是每秒钟启动5个线程。
- 循环次数:1个线程执行几次
- 延迟创建线程直到需要
- 调度器
- 持续时间:如果循环次数是永远,持续时间会被应用,表示持续多少秒后线程结束
- 启动延迟:线程在几秒之后启动
- 线程组的执行顺序
- 并发执行:多个线程同时执行,默认是并发执行
- 顺序执行:多个线程顺序执行,在最顶层的测试计划界面,勾选独立运行每个线程组,就可以开启顺序执行
- 特殊的线程组
- setUp线程组:最优先执行的线程组,创建方式也是 右击 执行计划,点击 添加 - 线程(用户)- setUp线程组
- tearDown线程组:最后执行的线程组,创建方式类似于setUp线程组
组件和元件
组件:实现单独的某个功能,有取样器、监听器等。
元件:多个类似功能组件的容器。元件也可以理解为组件的一种,下面把它们统称为组件
jmeter中的组件
在之前的入门案例中,接触了取样器和监听器,它们是jmeter中发送请求和分析响应的基本组件,jmeter还提供了许多其它组件,支持把一系列的请求组合起来,模拟用户的行为。
jmeter中的基本组件:
-
取样器:向服务器发送请求并接收响应的最小单位,jmeter支持不同的取样器,通过这些取样器,jmeter可以支持多种协议,常用的取样器有HTTP取样器、JDBC取样器、FTP取样器
-
监听器:对测试结果进行处理和可视化展示的一系列组件,常用的监听器有查看结果树、聚合报告
用于增强取样器的组件:
-
配置元件:配置其它组件的相关信息,常见的配置元件:HTTP信息头管理器、CSV数据配置、用户自定义变量
-
逻辑控制器:控制取样器执行顺序的组件,常用的逻辑控制器:if控制器、foreach控制器、循环控制器
-
定时器:在操作之前设置等待时间,常见的定时器:同步定时器、常量和吞吐量定时器
-
前置处理器:用于取样器执行之前对请求进行处理
-
后置处理器:用于取样器执行之后对得到的响应进行处理,常见的后置处理器:JSON提取器、正则表达式提取器、Xpath提取器
用于增强监听器的组件:
- 断言:对响应结果进行判断,场景的断言组件:响应断言、大小断言、JSON断言、
组件的作用域:组件可以被配置到测试计划下,也可以被配置到线程组下,被配置到哪里,作用域就是哪里。
组件的执行顺序
-
同一作用域下不同组件执行顺序:配置元件 --> 前置处理器 --> 定时器 --> 取样器 --> 后置处理器 --> 断言 --> 监听器
-
同一作用域下相同组件的执行顺序:从上到下依次执行
使用案例
在这里,通过实际的需求来学习jmeter中的常用组件
需求1:为HTTP请求添加请求头
这个需求需要使用http信息头管理器来完成。
http信息头管理器:配置元件的一种,配置HTTP协议的请求头
- 作用域:如果位于测试计划目录内,作用于测试计划下的所有线程组,如果位于线程组内,作用于线程组
案例:

需求2:把多个http请求的相同信息提取出来,集中管理
实现这个功能,需要使用"http请求默认值"组件
http请求默认值:配置元件的一种,作用是管理多个http请求的相同信息。
- 使用步骤:把多个http请求的相同信息写入到当前组件中,然后,在同一个测试计划下,新创建的HTTP请求就不再需要填写公共信息
案例:

需求3:把多处使用到的数据提取为变量,集中管理
这个需求使用"用户自定义变量"来实现。
用户自定义变量:配置元件的一种,在当前组件中配置键值对,在其它地方,使用 ${变量名} 的方式来引用变量
- 优点:通过用户自定义变量,来实现参数化。参数化是指动态地获取、设置或生成数据,是一种由程序驱动代替人工驱动的数据设计方案,提高脚本的编写效率和编写质量
案例:
- 配置变量:

- 使用变量:

需求4:发送10个请求,要求每个请求都有自己独立的数据
实现这个功能,需要从csv文件中读取数据,这需要使用"CSV 数据文件设置"
CSV数据文件设置:配置元件的一种,用户如果想要把数据放到csv文件中,需要使用当前组件。
-
使用方式:配置csv文件的文件名、文件编码、字段名称、分隔符,然后线程每执行一次,就会去csv文件中读取一行数据,用户再使用 ${变量名} 来引用csv文件中的数据。
-
优点:通过从csv文件中读取数据,把测试数据和测试脚本分离,使得测试用例更加灵活,并且可以实现参数化
案例:
- 设置csv文件的属性

- csv文件中的内容

- 引用csv文件中的数据

注意:
- 编写csv文件:文件中不能有表头,第一行就是数据,字段之间以逗号分割,文件使用utf-8编码。也可以使用txt文件来代替,因为csv文件本身就是普通的文本文件
- csv文件的局限性:字段中不能出现分隔符,使用反斜杠转义也不可以
- 配置线程的运行次数:线程运行一次,读取jmeter中的一行数据,下一次运行时读取下一行,所以运行次数最好和csv文件中的行数相对应,
使用csv文件时线程组的配置方式
在上面的案例中,需要把csv文件中的行数和线程的运行次数对应起来,才能保证线程正好能读取完csv文件中的数据,但是这样在实际使用过程中会很麻烦,所以现在提供一种方式,使得线程组可以在读取完csv文件后自动结束。
配置方式:
-
第一步:在线程组界面:配置线程的循环次数为永远
-
第二步:在CSV数据文件设置界面:
- 配置"遇到文件结束符再次循环"为false,表示只读取一次;
- 配置"遇到文件结束符停止线程" 为true,因为之前已经配置了线程的循环次数为永远,所以在这里,当csv文件被读取完时,线程就会停止循环
案例:
- 线程组界面

- csv设置界面

需求5:判断请求结果是否正确
这个需要需要使用"断言"来实现。
断言:用于判断请求结果的组件。断言和取样器类似,它们是平级关系,断言功能包含一系列组件,每个组件都可以执行不同类型的断言,包括JSON断言、大小断言等
案例:配置一个json断言,判断返回值中code字段的值是200.
- 配置方式:

- 断言结果

需求6:获取上一个请求返回的数据
需求:用户成功登录后,服务器会返回一个token,用户使用这个token,可以查看当前用户的详细信息。
实现这个需求,需要使用后置处理器下的JSON提取器。
后置处理器:在请求完成之后对响应结果进行处理。
案例:在这里使用后置处理器中的JSON提取器,提取服务端返回的json数据,把数据放到变量中,然后在下一个请求中引用变量。
第一步:配置请求的后置处理器,需要在取样器节点下添加后置处理器,只有这样后置处理器才能作用于取样器

第二步:在第二个请求中引用后置处理器中的变量

需求7:如果上一个请求不正确,下一个请求不执行
需求:用户登录成功后会返回一个token,在这里需要判断,如果用户没有登录成功,那么就不执行下一个请求。
实现这个功能,需要使用逻辑控制器。
逻辑控制器:控制它内部所有取样器的执行流程,取样器需要是逻辑控制器的子节点,然后才能生效。
案例:在这里选择使用逻辑控制器中的IF控制器来实现这个功能,将下一个请求放到IF控制器之中。
- 配置IF控制器:

注意,要取消勾选Interpret选项,在案例中使用到的变量是上一个节点的JSON提取器中提取出来的。
需求8:上传文件
需求:使用jmeter的http取样器,发送上传文件的请求
实现这个需求,需要使用http取样器中的"文件上传"tab,输入文件名、http请求中文件对应的参数即可。
案例:

需求9:后置处理器之正则表达式
需求:在http请求的响应中,会返回一个url,现在需要解析出这个url中的路径信息。
实现这个需求,需要使用后置处理器中的正则表达式提取器,配置正则表达式,提取数据中的内容
案例:

案例讲解:
- 第一步:配置需要从响应的哪部分中提取数据
- 第二步:配置提取出的数据存储到哪个变量中
- 第三步:配置正则表达式,要注意,提取的数据是正则表达式中被括号包裹的数据,相当于分组匹配,在这里的正则表达式是
https?://[^/]+(/[^?]+)
,这个正则表达式用于提取url中的路径部分 - 第四步:模板,表示提取正则表达式中第几个分组的数据,在这里是
$1$
,表示提取正则表达式中第一个括号中的数据。
扩展:提取正则表达式中的最后一节路径:https?://[^/]+/?.*/([^?]+)
需求10:if控制器 判断变量是10的倍数
在if控制器中写上表达式:${__jexl3(${pageNo} % 10 == 0)}
,在这里,pageNo就是变量
需求11:循环控制器和计数器
循环控制器是逻辑控制器的一种,计数器是配置元件的一种,联合使用,可以有for循环的效果,但是要注意,计数器必须放在循环控制器中才会在循环控制器的范围内生效。
需求12:跨线程共享变量
实现这个需求的步骤:
- 第一步:添加一个后置处理器,叫做BeanShell,在里面写入代码:
${__setProperty(var, ${var})}
,在这里,var是要被跨线程共享的变量 - 第二步:在其它线程中引用当前变量:
${__P(var)}
原理:在jmeter中,变量是线程共享的,属性是全局共享的,这里把变量设置为一个属性了。
需求13:每两秒钟发送一次请求
使用 定时器 - 固定定时器
来做
需求14:从json中提取出一个数组然后遍历这个数组
实现这个需求的步骤:
- 第一步:配置一个JSON提取器,难点在与JSON提取器中路径的配置,这里提供一个案例:
$.data[*].avatar
,表示提取data中所有名为avatar
的变量,并且注意Match No
输入-1,表示提取所有变量。 - 第二步:配置ForEach控制器,有几个输入性:
- 变量前缀:JSON提取器中设置的变量,
- 开始循环字段:0
- 结束循环字段:
${__javaScript(${变量名_matchNr})}
,这是jmeter提供的变量 - 输出变量名称:forEach控制器输出的变量名称
- 第三步:在ForEach控制器的内部使用变量。
其它常用组件
定时器:可以实现时间模式相关的性能测试。
- 同步定时器:Synchronized Timer,用来保证取样器在同一时刻向服务器发起负载
- 常量吞吐量定时器:高频率的访问服务器,1s钟访问多少次,持续多少秒,
取样器:
- 数据库取样器:直连数据库,向数据库发送请求并接收响应
在命令行运行脚本
Jmeter有两种运行模式:
- GUI模式:主要用来编写和调试测试脚本
- 命令行模式:对负载机的资源消耗会更小,用来实现高并发和压力测试
在命令行执行测试脚本:jmeter -n -t ${scriptFile} -l ${logFile} -e -o ${dir}
- 参数讲解
- -n 无图形化运行
- -t 被运行的脚本
- -l 将运行信息写入日志文件
- -e 生成测试报告
- -o 指定报告输出目录
案例:jmeter -n -t blog-api_最热文章.jmx -l j.log -e -o output
,在output目录下可以查看jmeter生产的html文件。
text
D:\apache-jmeter-5.5>jmeter -n -t blog-api_最热文章.jmx -l j.log -e -o output
Creating summariser <summary>
Created the tree successfully using blog-api_.jmx
Starting standalone test @ April 6, 2024 4:41:58 PM CST (1712392918522)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary + 121 in 00:00:01 = 101.8/s Avg: 53 Min: 10 Max: 155 Err: 0 (0.00%) Active: 10 Started: 10 Finished: 0
summary + 879 in 00:00:04 = 201.7/s Avg: 47 Min: 6 Max: 127 Err: 0 (0.00%) Active: 0 Started: 10 Finished: 10
summary = 1000 in 00:00:06 = 180.2/s Avg: 47 Min: 6 Max: 155 Err: 0 (0.00%)
Tidying up ... @ April 6, 2024 4:42:04 PM CST (1712392924365)
... end of run
性能测试结果报表:html文件

message on port 4445
summary + 121 in 00:00:01 = 101.8/s Avg: 53 Min: 10 Max: 155 Err: 0 (0.00%) Active: 10 Started: 10 Finished: 0
summary + 879 in 00:00:04 = 201.7/s Avg: 47 Min: 6 Max: 127 Err: 0 (0.00%) Active: 0 Started: 10 Finished: 10
summary = 1000 in 00:00:06 = 180.2/s Avg: 47 Min: 6 Max: 155 Err: 0 (0.00%)
Tidying up ... @ April 6, 2024 4:42:04 PM CST (1712392924365)
... end of run
性能测试结果报表:html文件
[外链图片转存中...(img-Qb2zCm3f-1743754695159)]