1.JMeter介绍
Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件。JMeter小巧轻便且免费,逐渐成为了主流的性能测试工具,是每个测试人员都必须要掌握的工具之一。
环境要求:
需要Java8或者更高的版本。
1.1 JMeter的下载
1)登陆JMeter的官网:Apache JMeter - Download Apache JMeter
2)点击要下载的版本即可自动下载
3)下载完毕后,进行解压
4)解压完毕后,就会出现如下文件
1.2 JMeter的打开
方式一:点击bat文件
点击进入bin文件夹
在bin文件夹下有一个jmeter.bat文件
双击运行后会先打开cmd窗口
然后就会启动jemter
方式二:配置环境变量(推荐)
将bin目录所在的文件夹复制下来
打开系统的环境变量配置
双击Path,进入Path环境变量的配置,先点击新建,然后复制刚刚的路径,最后点击确定。
然后我们使用win+r键,输入cmd打开命令提示符窗口
输入jmeter后能运行出出来则说明配置成功了。
1.3 JMeter配置中文
方法一:手动更改
依次点击左上角的"Options" -> "Choose Language" -> "Chinese(Simplified)"
但是这种方法不是一劳永逸的,下次重新登陆还是英文的。
方法二:更改配置文件(推荐)
在bin文件夹下有一个jmeter.properties文件。
使用Ctrl+f查找language
将找到的第四个language=en改成zh_CN
然后将文件保存,重新打开JMeter就是中文了。
1.4 JMeter元件作用域和执行顺序
在JMeter中,元件的作用域和执行顺序是非常重要的概念。
作用域:
JMeter元件的作用域主要由测试计划的树形结构中的元件父子关系来确定。
执行顺序:
取样器(sampler)元件内组件不依赖其他元件就可执行,因此取样器不存在作用问题元件作用域只对它的子节点有作用,其他作用域默认根据测试计划中树形结构来定;
2. JMeter基本使用流程
这里先带大家简单见一下JMeter怎么使用,后面每个组件都会详细介绍
1)点击左上角的编辑->添加->线程(用户)->线程组
创建完之后如下所示:
在创建完线程组之后,我们就可以来性能测试了,现在我们要选择进行性能测试的对象,就以测试网页http://www.baidu.com/s?ie=utf-8&wd=jmeter为例。
在线程组下点击添加->取样器->HTTP请求
创建完成后,填写上如下内容:
在设置完成后,点击左上角的运行。
此时会跳出来一个弹窗,意思是我们需要先保存才能运行。
我们保存完毕后,再次点击运行,可以看到下面两个框变红了,说明他们正在运行。
运行的结果我们现在是没法直接看到的。
我们点击线程组->添加->监听器->查看结果树
此时再次点击运行,就可以发现结果树页面下包含一个HTTP请求。
在取样器结果中,我们就可以查看本次测试的结果,并且能查看请求报文和响应数据。
如果想测试并发场景,可以将线程组中的线程数修改一下,这里的线程数指的其实是虚拟用户数,JMeter会以虚拟用户的身份去访问这个网站。
设置好线程数后,回到结果树,为了不让上一次的查询结果对这一次产生干扰,我们点击清除按钮,进行清除。
清除之后再次运行,我们就能看到出现了10个HTTP请求。
3. 重点组件
3.1 线程组
添加线程组
参数解释
右边的选项从上往下,依次来看
- 名称:线程组的名字,可任意填写。
- 注释:对线程组的介绍,可写可不写。
- 在取样器错误后要执行的动作:即请求错误后要执行的动作,若在当前线程组下存在多个HTTP请求中,某个请求出错了,会根据选项来执行后续的动作,默认为继续执行。
- 线程数:虚拟用户数(并发数),用于设置发送的请求次数。
- Ramp-Up时间(秒):性能测试运行时间,单位为秒,若线程数为10,Ramp-Up时间为2,意思是发送10个请求必须在2秒内完成。
- 循环测试:测试执行的次数
- 配置指定次数:控制脚本循环执行的次数(最终运行次数 = 循环测试数 * 线程数)
- 配置循环永远:需要调度器配合使用,否则就是死循环(设置脚本执行时间,延迟启动时间:脚本等待指定时间才能运行)
3.2 HTTP取样器
添加取样器
参数解释
- 协议:向目标服务器发送HTTP请求协议,可以是HTTP或HTTPS,默认为HTTP
- 服务器名称或IP :HTTP请求发送的目标服务器名称或IP
- 端口号:目标服务器的端口号,http协议端口号80,https端口号443
- 请求方法:发送HTTP请求的方法,可用方法包括GET、POST、HEAD、PUT、DELETE等
- 路径:目标URL路径(URL中去掉服务器地址、端口及参数后剩余部分,在http://www.baidu.com/s?ie=utf-8&wd=jmeter例子中,路径就是/s)
- 内容编码:编码方式,默认为ISO-8859-1编码
- 参数:在请求中发送的URL参数,可以将URL中所有参数设置在本表中,表中每行为一个参数(对应URL中的 name=value),在我们的例子:http://www.baidu.com/s?ie=utf-8&wd=jmeter中,参数有两个,分别为ie=utf-8和wd=jmeter。
以http://www.baidu.com/s?ie=utf-8&wd=jmeter网页为例,HTTP取样器填写如下:
注意:
参数可以点击添加直接配置。
也可以使用消息体数据,采用json格式进行配置。
3.3 查看结果树
创建查看结果树
参数介绍
重点关注取样器结果,请求,响应数据这三个选项。
当我们请求失败时,如我们设置以一个错误的域名,取样器结果如下:
请求中的内容如下,我们可以在这里检查是否是请求的IP或参数不正确。
因为这个请求是没有响应的,所以响应数据显示HttpHostConnectException,即http连接异常。
注意:
左上角有一个下来菜单,可以选择展示方式,默认Text为文本格式。不太好看,我们可以设置为Json格式。
3.4 HTTP请求默认值
如果你要多次访问同一个web系统,每次都需要重新输入协议,地址,端口等内容非常麻烦,可以设置HTTP请求的默认值。
配置
此时我们再创建一个新的HTTP请求,这个新的请求中什么内容都没有
直接点击运行,可以发现该HTTP请求居然请求成功了。
这就是HTTP请求默认值的好处,可以帮我们省去很多重复的填写。
之所以叫他默认值,因为如果我们自己手动配置,就使用手动配置的,如果请求中没有配置,就去请求默认中取。
3.5 HTTP信息头管理器
一个http请求会发送请求到服务器,请求里面包含:请求头、请求正文、请求体,请求头就是信息头,当我们配置好信息头后,配置的信息头会跟随HTTP请求一起发送给服务器,常用于传送cookie,token或者伪造头的场景。
设置HTTP信息头管理器
假设现在有这样一个网站,这个网站在登陆成功之后,会发送给客户端一个报文,报文中包含如下字段,其中有个字段data。该网站要求每次在该网站申请服务(发送http请求)时,都要携带data字段,不携带的说明不是该网站的登陆用户,返回一个401错误页面。
在发送http请求时,我们添加一个名为user_token_header的字段,值就是上面提到的data。
当添加这个字段后再次发起一个http请求时,就可以成功访问该页面了。
也就说我们要想访问这个网站,就必须携带user_toke_header,所以我们就可以使用HTTP信息头管理器。
添加完之后,我们再点击运行,可以发现请求头中自动帮我们添加了user_token_header。
3.6 JSON提取器
在HTTP信息头管理器中,我们说到了当有一个网站在登陆成功后会发送一个data,当我们需要使用登陆页面才能使用的功能时,每次的请求都需要添加data字段。
那么现在的问题时,网站每次发送的data都不一样,那么我们就需要手动修改,非常麻烦,所以我们可以使用JSON提取器来提取其中的data字段,然后将他放到信息头管理器中。
JSON提取器的作用:接⼝响应成功,通过提取返回值对应字段,可用于其他接口的参数配置
添加JSON提取器
提取响应数据肯定是在发送数据之后,所以叫后置处理器。
介绍
其中json提取需要通过json操作符。
如果说返回的json如下所示,我们想要提取出data对应的值。
首先使用$选中根据元素,即{},使用.data就能选中到data。
我们可以打开左边的JSON JMESPath Tester来检查我们的json提取是否正确。
我们将json提取的内容输入进去,然后点击右边的Test即可进行测试,测试的结果正是我们想要提取的,所以这个json提取的内容就是正确的。
所以我们的JSON提取器用就可以写如下内容。
然后还要修改HTTP信息头管理器,以前的user_token_header是写死的,而现在修改之后就能以不变应万变。
如果现在响应的data修改了。
请求头中的user_token_header就能提取出data了。
如果有多个http请求,json提取器提取的字段会发生覆盖
json提取器的原理是提取同级中从上往下http响应中的字段。
假设下面场景:博客系统,其中“登陆”页和“列表页”都会返回一个data,后面在这个系统中所有的http请求都需要携带“登陆”页返回的data,否则认为请求非法。
执行过程:
- “提取用户登陆凭证”是一个json提取器,我们设置好之后,就可以提取到往后遇到的http响应中的data字段。
- “登陆”是一个http请求页,当发起请求后,会返回一个http响应,其中包含data字段,那么json提取器就提取到了这个data,此时变量token中保存的就是“登陆”页的data字段。
- “列表页”包含子集,所以先执行他的子集“HTTP信息头管理器”,此时会提取变量token中的字段,也就是“登陆”页的data,这个信息头将作为列表页的http请求头字段,也就是说“列表页”的http请求中user_token_header中的data是“登陆”中欧的data,所以“列表页”可以成功登陆。
- “列表页”是一个http请求页,他的响应中也包含了data,所以json提取后,将token中的值覆盖为“列表页”的data。
- “用户信息”中的“HTTP信息头管理器”提取token,提取到的是“列表页”的data字段,没有“登陆”的data,所以“用户信息”访问失败。
上面的过程中出现了一个问题:“列表页”中的data字段会覆盖掉“登陆”中的data,最终导致访问“用户信息”失败。
解决办法也很简单,改变一下执行顺序即可,json提取器是“登陆”子集,也就是它只会提取“登陆”中的data,然后将提到的值保存至“HTTP信息头管理器”中的token变量。
而“列表页”虽然有data,但是没有了json提取器,所以“HTTP信息头管理器”中的token值就不会改变了。
如何编写json提取
我们想要提取json中的数据,就要用到json的操作符,这篇文章json操作符详细的介绍了json操作符的使用,感兴趣的同学可以取看看。
3.7 用户定义的变量
还是以上面的博客系统为例,假设“列表页”的功能是保存了多篇博客,每个博客以blogid为标识符区分,我们创建一个http请求命名为详情页,每个详情页都是访问具体的某一篇博客。
如下图所示,其中blogid=1993,表示要访问的是blogid为1993的博客。
假如说现在有200个详情页,分别都是不同的blogid,我想让他们的blogid都变成1993,如何实现?
如果手动一个一个修改就太麻烦了,我们可以创建一个用户定义的变量来解决这个场景。
添加用户定义的变量
然后我们创建一个id,值为1993。
然后将所有详情页中的blogid设置为${id},现在所有的blogid的值都为id了,如果后面想把所有的blogid设置为1992,则直接修改id,非常方便。
3.8 JSON断言
接口发送请求成功,响应码为200并不能完全代表接口请求成功,我们更多需要关注接口响应数据是否符合预期。
我们可以使用JSON断言来判断响应数据是否与预期数据相等。
添加JSON断言
添加JSON断言配置
注意:
- 1)若不选Additionally assert value,表示添加断言值,则可用来判断字段是否存在,比如仅要判断data字段是否存在,则不选。
- 2)选择Additionally assert value,则必须添加Expected Value期望的断言值(精确匹配,区分大小写)
- 3)若不选Match as regular expression正则匹配,则Expected Value必须填写完整,少一个字符都会导致断言失败
- 4)若选择Match as regular expression支持正则匹配,则Expected Value可以仅写上部分关键词即可断言成功
如果登陆成功,会返回一个非空的data,那么我们就可以通过data来判断登陆是否成功(不能简单以返回码来判断成功,可能存在返回码为200,但是响应信息为登陆错误的场景)
使用正则表达式,\S表示能匹配一个非空的字符,+表示匹配一个或多个,两个结合在一起就变成了能匹配一个非空字符或者多个非空字符,这样就能成功匹配data了。
3.9 同步定时器(集合点)
我们前面介绍了线程组的概念,我们说的线程指的就是虚拟用户,假如说我们现在将线程数改为5。
每个线程组都会执行一遍下面的测试案例,也就是说一共会执行5遍。
在运行之后,右上角会显示此次运行的时间,一共是1秒钟,那么现在的问题是,这5个线程是同时执行的(并发),还是执行完一个之后再执行第二个(串行)。
我们点击运行时间右边的黄色三角形就可以查看进程状态。
结论:这5个进程是串行完成测试计划的,谁先准备好谁先执行。
如果我们想要让这5个进程同时执行测试计划,即我们要实现并发,我们需要添加同步定时器。
添加同步定时器
配置同步定时器
模拟用户组的数量:每次释放的线程数量。当设置为0时,等同于线程组中设置的用户数量。
当配置完成后,我们再来看一下添加完同步定时器之后有什么区别
可以看到所有线程都准备好了才会发送请求,而每个线程发送请求和完成的时间都不相同,所以有了先后顺序。
如果模拟用户组数量超过了线程组里配置的线程数?
线程组中配置线程数为5,我们将模拟用户组数量设置为50后,再次进行测试。
可以看到最终卡住了,因为我们设置的50个,他就会等待50个线程,但是实际上只有5个线程,它要等到剩下的45个线程都准备好才会继续执行。
结论:模拟用户数不能超过线程组中配置的线程数
如果模拟用户组数量小于线程组里配置的线程数?
线程组中配置线程数为5,我们将模拟用户组数量设置为3后,再次进行测试。
可以看到虽然模拟用户组只配置了3,但是5个线程组都会准备。但是当前面4个线程准备好之后,此时共有4个线程准备好,4大于等于3,就直接发送了,而第5个线程还在等待,此时只有1个线程在等待了,不能满足大于等于3,所以5号线程一直在等待另外2个线程来才能发送。
结论:当准备好的线程数>=配置数量,就直接发送请求,所以配置的数量小于线程数时,最好把循环打开,避免最后一次未准备好的线程数量达不到并发数陷入一直等待的状态。
3.10 事物控制器
在聚合报告中,可以看到有一个表格,其中Label表示标签样本,而一个Label就可以看成是一个事物。
现在我们想让“登陆”和用户信息”合并成一个事物,我们可以通过事物控制器来完成上面的功能。
添加事物控制器
当创建完成之后,我们将其命名未“登陆事物”,然后将“登陆”和“用户信息”都放在“登陆事物下”。
那么将这两个合并成一个“事物”有啥作用呢?
此时我们再次运行并打开聚合报告,可以看到“登陆事物”会将“登陆”和“用户信息”的平均时间、中位数等都加起来,这样就能方便我们进行性能分析。
在进行页面性能测试或API性能测试时,事物控制器可以帮助测试人员更准确地评估系统性能,尤其是在涉及多个接口或操作的复杂场景中。例如,在订单提交的过程中,可能需要调共多个接口,并且某些接口可能依赖于前⼀个接口的结果。在这种情况下,使用事务控制器可以将这些接口统一视为一个事务进行性能测试,从而得到更接近真实场景的性能测试结果。
3.11 CSV数据文件设置
以登陆接口为例,当我们执行登陆接口的性能测试时,手动配置了用户名和密码为固定的username和password,然而实际使用中不可能只有一个用户登陆,为了模拟更真实的登录环境,我们需要提供更多的用户username和password来实现登录操作。
添加CSV数据文件设置
配置CSV数据文件设置
- 文件名:填写csv文件的路径。建议使用绝对路径。
- 文件编码:UTF-8
- 变量名称:从csv数据文件中读起的数据需要保存到的变量名,有多个变量使用逗号分隔。
- 是否忽略首行:是否从csv数据文件第一行开始读取。
- 分隔符:要求与csv数据文件中多列的分隔符一致。
- 遇到文件结束符再次循环:若选择为True当数据不够的时候会从头取。若选择False,则需要勾选下面的配置,遇到文件结束符停止线程,这里如果不勾选,请求将会报错。
其中的csv文件就是我们常说的excel,csv文件内容如下,第一列是账号,第二列是密码。
我们将读取到的账号和密码保存至username和password两个变量中
那么我们就可以配置CSV数据文件如下图所示。
既然我们将用户名和密码都保存至username和password中,所以登陆页面就可以去这两个变量中取了。
都配置好之后,点击执行,第一个登陆请求中的username就被设置为了zhangsan,password为123456。
而第二个登陆请求中的username变成了lisi,password为123456
3.12 HTTP Cookie管理器
我们前面说到的登陆凭证是在登陆之后,响应报头中包含了一个data字段,往后必须携带data字段才视为登陆用户。
而另一种场景就是使用Cookie来验证是否是已登录用户。如下图所示,当请求头中没有携带Cookie时,虽然能返回响应,但是响应报头中的userId为0,并且username为空,这表示未登录。
当我们携带了Cookie再访问同样的接口时,此时返回的就是正确的userId和username,表明我们是已经登陆的用户,可以享受该网页的服务了。
创建HTTP Cookie管理器
Cookie管理器的作用是:自动获取接口中的Cookie信息,带入到其他接口请求中。
如上图所示,登陆有两个子集,先执行了登陆-0,此时它的http响应数据中收到了一个Cookie,而这个Cookie会被Cookie管理器接收到;在执行登陆-1时,会将接受到的Cookie放到登陆-1的请求头体重。
在没有添加Cookie管理器时,http请求体如下:
而添加了Cookie管理器之后的http请求体如下:
Cookie管理器会自动提取Cookie,如果有需要手动添加的场景,直接添加即可。
3.13 插件
在真实企业压测场景中,我们通常为一点一点的逐步增加线程数,比如我们的期望最大并发为10000,但是我们不能一上来就测试10000,而是慢慢加,比如100->1000->3000->6000->8000->10000。
因此需要安装新的插件来支持线程数的配置,下载连接:JMeter插件
点击“plugins-manager.jar”就可以下载了。
将下载好的文件放到Jmeter下的lib文件夹下的ext文件夹
我们重新打开jmeter之后,就可以发现右上角多了一个小蝴蝶。
当我们点击之后
我们安装两个插件,监听器和线程组
1)监听器插件
2)线程组
点击右下角的Apply Changes and Restart JMeter就可以下载了,下载完成后会自动重启JMeter。
当添加完成后,可以看到线程选项中多了很多。
监听器中也多了。
3.13.1 Stepping Thread Group
创建后如下所示:
- This group will start:启动多少个线程,同线程组中的线程数
- First,wait for:等待多少秒才开始压测,一般默认为0
- Then start:一开始有多少个线程数,一般默认为0
- Next,add:下一次增加多少个线程数
- threads every:当前运行多长时间后再次启动线程,即每一次线程启动完成之后的的持续时间;
- using ramp-up:启动线程的时间;若设置为5秒,表示每次启动线程都持续5秒
- Then hold load for:线程全部启动完之后持续运行多长时间
- finally,stop/threadsevery:多长时间释放多少个线程;若设置为5个和1秒,表示持续负载结束之后每1秒钟释放5个线程
启动方式:(重点与下面三个参数有关)
这三个选项表示:每隔30秒添加10个线程,这些线程要在5秒内准备完成。
结束方式:(重点与下面两个参数有关)
每隔1秒结束5个线程。
通过下面的图也可以看到,其中的横坐标表示秒数,纵坐标表示的线程数。
3.13.2 监听器
最常用的监听器如下图所示。
点击运行之后,折线图如下所示,他会动态的展示线程数与时间的关系图。
响应时间如下图所示:
吞吐量如下所示:
可以看到响应时间增大时吞吐量就会降低,即响应时间与吞吐量呈负相关。
4. 性能报告
在性能测试完毕之后,我们需要讲测试结果展示出来,JMeter可以帮助我们生成性能报告。
JMeter测试报告是⼀个全面且详细的文档,它提供了关于测试执行结果的详细信息,帮助用户全面评估系统的性能并进行性能优化。
生成性能测试报告的命令:
Jmeter -n -t 脚本文件 -l 日志⽂件 -e -o 目录
- -n :无图形化运行(不打开JMeter
- -t : 被运行的脚本
- -l : 将运行信息写入日志文件,后缀为jtl的日志文件
- -e :生成测试报告
- -o :指定报告输出目录
注意:日志文件和目录可以不存在,若为已经存在的情况下需要保证内容为空,否则会出现错误!
例如:我们要将“第一个性能测试.jmx”生成性能测试报告,并将它放到rizhi目录下,命令如下:
生成完之后会发现rizhi目录下多了一个index.html文件。
双击运行:
5. 性能分析
通过三大指标来分析性能问题
4.1 响应时间
如果响应时间超过了要求,代表系统到了瓶颈。
注意事项:分析在多少线程的情况下发生了超标
响应时间变化的原因:
- 系统不稳定,有时快有时慢
- 随着并发压力变大而慢慢变慢,响应时间变高
4.2 错误率(可靠性)
高并发场景下,系统是否能够正常处理业务
要求:99.99%可靠,99.9999%
错误率高的原因:
- 接口请求错误
- 服务器无法继续处理,达到了瓶颈(代码写的不好,内存泄漏、硬件资源等)
- 后端系统限流(系统⾥配置了不能超过多少并发)、熔断、降级
什么是熔断、降级?
- 熔断:防止系统因某个服务的故障而整体崩溃。当电商平台上用户支付时,收银台发现某个支付渠道,如微信支付失败率突增,超时严重,那么就可以临时把这个支付方式熔断掉。
- 降级:主动关闭⼀些非核心功能,以确保核心功能的正常运行。某次腾讯视频挂了的时候,用户名称默认显示腾讯用户,这也是一种降级方式,用兜底名称做展示。
4.3 吞吐量
吞吐量越大,性能越好;吞吐量相对稳定或者变低,可能系统达到了性能瓶颈
吞吐量变化规律:
- 波动很大:代表系统性能不稳定。
- 慢慢变高,再趋于稳定:和并发量强相关。如果并发量小于吞吐量,慢慢增大并发量,吞吐量也会随之增加。
- 慢慢变低,并发量也减少了:要么说明性能测试要结束了,并发减少;也可能是系统变的卡顿,从而导致响应时间变慢,导致单个线程发起的并发量变少。