1. 前言
压力测试是每一个Web应用程序上线之前都需要做的一个测试,他可以帮助我们发现系统中的瓶颈问题,减少发布到生产环境后出问题的几率;预估系统的承载能力,使我们能根据其做出一些应对措施。所以压力测试是一个非常重要的步骤,下面带大家来使用一款压力测试工具Jmeter。
2. Jmeter的下载
因为Jmeter是使用JAVA写的,所以使用Jmeter之前,需要安装JAVA环境。
(1)去 Oracle官网下载符合版本要求的JDK;
JDK官网下载
(2)去Apache官网下载 Binaries 系列的最新 jmeter.tgz 包;
Apache Jmeter官网下载链接:http://jmeter.apache.org/download_jmeter.cgi
(3)下载完毕后解压,解压后目录结构如下图;
bin:该目录包含启动和执行其他 Jmeter 操作的可执行文件。
docs:该目录包含用户指南。
extras:该目录包含各种使用样例。
lib:该目录包含 Jmeter 所需的 JAR 包(也可以添加其他的 JAR 包)。
Printable_docs:里面是一些可打印的文件。
3. Jmeter的运行
(1)在终端进入解压目录的 /bin/ ,通过 sh jmeter 命令来启动 Jmeter。
Don't use GUI mode for load testing:这是一段提示信息,不要使用GUI模式进行负载测试,要使用NON GUI模式。
(2)执行上述操作后,弹出下面的Jmeter图形化界面 ;
默认是英文显示,我们可以将语言设置为【简体中文】;
选择Options-->Choose Language-->Chinese(SimpSimplified);
⚠️注意:
启动之后会有两个窗口,一个终端窗口,一个Jmeter的 GUI 窗口,都不能关掉。
4. 一个简单的请求测试
4.1 创建线程组
(1)创建流程:测试计划-->右键-->添加-->hreads-->线程组
线程组用来模拟用户。一个线程组模块可以包含多个线程,每个线程代表一个用户,这样可以模拟高并发下的请求,并根据网站的响应信息来判断网站的相关性能。
(2)设置循环的次数和并发的线程数量;
在取样器错误后要执行的动作
继续:某一个请求遇到错误后,其他请求继续执行。我们在大量用户并发的时候,某个请求失败属正常现象。
启动下一进程循环:如遇到错误后面的请求将不再执行,等下一轮再开始执行。例如线程组中包含登录和退出两个请求,若登录请求失败,退出请求将不再执行,等下一次重新迭代,从登录开始执行。
停止线程:遇到错误就停止线程再也不执行了。例如线程组中有50个线程,其中某一个线程的某个请求遇到错误即停止线程不再执行,剩下49个线程继续执行。若线程错误的比较多,剩余的线程就较少,此时负载数量就不足了,测试结果不满足测试要求,因此一般不会勾选此项。
停止测试:某个线程某个请求遇到错误,停止所有线程,也就是停止整个测试,但是线程中的余下的请求还是会执行完再停止。例如线程组中包含登录和退出两个请求,其他线程遇到错误,现在要全部停下来,线程组退出请求还是会执行,然后再停止测试。
立即停止测试:遇到错误立即停止所有线程,即整个测试。
线程属性
线程数:一个线程相当于一个虚拟用户。
Ramp-Up时间:线程启动开始运行的时间间隔,单位秒。即所有线程在多长时间内全部启动。例如线程60个,Ramp-Up时间设置为20s,那么每秒启动60/20=3个线程,不填写默认设置为0,即所有线程在开启场景后立即启动。
循环次数:勾选永远,将一直执行,除非手动停止或崩溃。
延迟创建现场直到需要
勾选,例如50个线程Ramp-Up时间为10s,那么每隔1s启动50/10=5个线程并运行下面的请求(状态为running);不勾选,测试计划开始后启动所有线程(状态为new),但是不立即执行下面的请求。例如50个线程Ramp-Up时间为10s,那么计划开始后所有线程全部就绪,但第一秒只有5个线程开始运行请求。实际应用中选择哪种都可以,不影响测试结果。
调度器
持续时间:测试计划持续多长时间。
启动延时:点击启动按钮后,仅初始化场景,不运行线程,等待延时时间到才运行。
⚠️注意:
线程组中循环次数和持续时间同时存在时,不会执行持续时间的设置。
如上述:100个线程循环一次持续时间为300s,此设置是无效的。设置持续时间时,循环次数应该勾选永远。
4.2 构造HTTP请求
(1)创建流程:线程组上右键-->添加-->取样器-->HTTP请求;
(2)配置我们需要进行测试的程序协议、地址和端口等;
Web服务器
协议:向目标服务器发送 HTTP 请求协议,可以是 HTTP 或 HTTPS ,默认为 HTTP ;
服务器名称或IP:HTTP 请求发送的目标服务器名称或 IP ;
端口号:目标服务器的端口号,HTTP默认端口号为80端口,HTTPS默认端口号为443端口;
HTTP请求
方法:发送 HTTP 请求的方法,可用方法包括 GET、POST、HEAD、PUT 等;
GET: 请求指定的页面信息,并返回实体主体。
HEAD: 只请求页面的首部。
POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。
PUT: 从客户端向服务器传送的数据取代指定的文档的内容。
路径:目标URL路径(URL中去掉服务器地址、端口及参数后剩余部分);
内容编码:编码方式,默认为 ISO-8859-1 编码,一般配置 utf-8;
自动重定向:当重定向时,自动跳转时,只针对GET和HEAD请求,自动重定向可以自动跳转到最终目标页面,但是Jmeter不记录重定向过程内容(在查看结果树中只能看到重定向后的响应内容);
跟随重定向:当重定向时,自动跳转时,自动重定向可以自动跳转到最终目标页面,但是Jmeter记录重定向过程内容(在查看结果树中既能看到重定向后的响应内容,也能看到重定向前的响应内容);
使用keepAlive :当该选项被选中时,jmeter 和目标服务器之间使用 Keep-Alive方式进行HTTP通信,默认选中。
对POST使用multipart/from-data :当发送HTTP POST 请求时,使用Use multipart/from-data方法发送,默认不选中。
参数:同请求一起发送参数,在请求中发送的URL参数,用户可以将URL中所有参数设置才在本表中,表中每行为一个参数(对应URL中的key=value),注意参数传入中文时需要勾选“编码”。
⚠️注意:
- 当所有的接口测试的访问域名和端口都一样时,可以使用该元件,一旦服务器地址变更,只需要修改请求默认值即可。
- GET和POST的区别。
4.3 添加HTTP头信息管理
(1)创建流程:线程组上右键-->添加-->配置元件-->HTTP头信息管理;
(2)点击HTTP头信息管理进行Header头的设置,因为我要传输的数据是json格式,所以就设置了 Content-Type:application/json;
4.4 添加断言
(1)创建流程:HTTP请求上右键-->添加-->断言-->BeanShell断言;
(2)进行脚本的编写,一般只需要修改框中内容即可;
根据响应的数据来判断请求是否正常,还可以配置错误信息;
4.5 添加查看结果树
(1)创建流程:线程组上右键-->添加-->监听器-->查看结果树;
(2)我们可以在框中的地方设置结果、请求和响应数据的显示类型;
4.6 运行
点击启动按钮,在查看结果树中查看结果。
5. 场景应用
场景一:数据的多样性
做接口性能测试过程中,经常需要用不重复的参数来进行测试,Jmeter提供的随机数函数很好的解决这个难题。Jmeter共提供了4种随机数分别为:数字随机数Random、日期随机数RandomDate、字符串随机数RandomString,变量随机数RandomFromMultipleVars,满足我们不同场景下的传参需求。
这里我们使用字符串随机数RandomString进行场景演示。为了保证数据的多样性,我们需要不同的手机号码来进行token的获取。
(1)在工具中选择函数助手对话框打开;
(2)找到 RandomString 函数,进行配置,点击生成,则在The result of the function is 中显示随机生成的字符串;
(3)为了获得随机的手机号,我们需要在HTTP请求中进行修改对应的消息体数据;
随机生成字符串的格式:${__RandomString(10,0123456789,)}
(4)点击启动,我们就可以在查看结果树中登录请求的请求体中看到随机生成的手机号;
场景二:响应数据的提取
方式一:JSON提取
JSON提取器属于Jmeter 的后置处理器, 所谓后置提取器就是请求结束后, 对响应结果进行变量提取, 提取变量是为了验证变量是否符合预期或者将变量值作为全局变量, 以供其他请求使用。
针对于当前场景,我们需要把登录请求获取的token作为退出请求的请求参数,为了整个项目的连贯性,需要用到JSON提取器来提取前一接口的响应数据,然后传递给下一接口。
(1)创建流程:HTTP请求上右键-->添加-->后置处理器-->JSON提取器;
(2)接着进行JSON提取器参数的配置;
标准写法为:$.key,其中 key 为返回结果 map 中的一个键,如果是多层则继续使用 .key 进行即可,如果遇到 key 的 value 值为一个 List ,则使用 .key[n],其中 n 为 List 中元素的编号。
(3)配置完成后,点击启动,在查看结果树中选择在JSON Path Tester,即可展开如下图的内容,输入表达式即可拿来测试书写的JSON提取器表达式是否能正常工作;
方式二:设置全局变量进行参数传递
实际场景是这样的,我们在做性能测试时,会涉及一个或多个线程组。而线程之间或接口之间会对某个参数有依赖性,那么我们需要将某个接口中的参数提取出来,供其他线程组或接口调用。这里就需要使用到__setProperty函数:
声明全局变量:${__setProperty(自定义变量名,${参数名},)}
引用全局变量:${__property(自定义变量名)}
(1)创建流程:测试计划上右键-->添加-->配置元件-->用户定义的变量;
自定义变量,与“测试计划”中定义的变量效果是一样的,这意味着使用“用户定义的变量”添加的变量都是全局性的,不管你把它放到哪个线程组位置。如果在其他地方定义了一个同样名称的变量,该变量的值会跟着更新。
(2)接着需要对多个地方进行配置,首先是登录请求中的BeanShell断言需要补充,用来把执行成功后获取到的 token 传给 newtoken ;
接着在用户定义的变量中进行配置;
⚠️注意:
引用已定义的变量:${变量名},比如: ${token};如果变量未定义的话,引用变量会直接返回表达式,比如变量 token 未定义,引用变量 ${token},返回值就是:${token}。
(3)我们需要再创建一个退出登录的HTTP请求,操作和之前一样;
(4)点击启动,在查看结果树中查看退出请求的请求体,发现获取到了token;
且与登录请求响应体中的token一致。
方式三:将数据写入到本地文件中
此时的场景是这样的,我们需要将登录请求中服务器返回的token获取并写入到本地文件中,供其它接口调用。这样在压测单接口时,不需要再进行登录,避免压测单接口时,登录接口对服务器造成额外影响。
(1)创建流程:线程组上右键-->添加-->取样器-->BeanShell取样器;
(2)编写脚本;
(3)点击启动,查看结果数中执行成功,在文件生成路径会创建对应文件,且数据都写入到文件中;
场景三:从文件读取数据
此时构建的场景是模拟多用户进行退出操作。真实情况下,不可能每进行一次退出请求,就去进行登录请求。我们需要拿到一批token来提供需要,这里,我们就需要从文件中读取所需参数了。
(1)我们再创建一个退出登录的线程组,用来测试是否从文件中获取到了token;
首先我们需要右键新的线程组-->添加-->配置元件-->CSV Data Set Config;
(2)配置CSV数据文件设置中的参数;
文件名
这里要包括文件的路径,在4.0版本中可以点击右侧的浏览按钮选择文件,会自动带上文件的绝对路径;
另外,当csv文件在Jmeter的bin目录或脚本目录时,只需给出文件名即可;
使用相对路径时,Jmeter默认先去bin目录下查找,然后去脚本目录下查找;
文件编码
默认使用当前操作系统的编码格式,如果文件中包含中文乱码时,可尝试utf-8、gbk等;
变量名称
csv文件中各列的名字(有多列时,用英文逗号隔开列名);名字顺序要与内容对应,这个变量名称是在其他处被引用的,所以为必填项。
分隔符
csv文件中的分隔符(用”t”代替tab键);一般情况下,分隔符为英文逗号,保持默认就行;
遇到文件结束符再次循环?
到了文件尾是否循环,True—继续从文件第一行开始读取,False—不再循环;
此项与下一项的设置为互斥关系,即true-false,或false-true;
遇到文件结束符停止线程?
到了文件尾是否停止线程,True—停止,False—不停止;
注意:当Recycle on EOF设置为True时,此项设置无效。
线程共享模式
All threads –所有线程,此元件作用范围内的所有线程共享csv数据,每个线程依次读取csv数据,互不重复;
Current thread group—当前线程组,在此元件作用范围内,以线程组为单位,每个线程组内的线程共享csv数据,依次读取数据,互不重复;
Current thread—当前线程,在此元件作用范围内,每次循环中所有线程取值一样;
(3)上述操作都完成后,点击运行,在查看结果树中查看结果;
可以看出,退出请求获取到了文件中的数据。