一、为什么要jmeter二次开发
虽然使用beanshell可以实现jmeter基本功能中无法实现的功能。
但是还是需要做一些二次开发:如果逻辑比较复杂,或者代码量比较大,用beanshell编写脚本成本会比较大。
在beanshell解释器里写代码没有提示,报错起来查找问题也比较困难。
另外在jmeter线程线程比较多会耗费本地资源,因为jmeter是去调beanshell的编译器的来执行脚本,会耗费比较多的资源。
开发插件的好处jmeter没有的支持协议的取样器,可以通过java完成。
二次开发的话,都是使用jar包的形式。做二次开发后把jar包放在jmeter下的Lib的ext目录下,添加java请求,在类名称这里会显示写的类文件。就可以实现相关功能。
虽然可以使用jmeter调用beanshell解释器来运行脚本,如果线程比较多,目标tps要求比较高的时候?
压力机设置的线程比较多会耗费本地资源,不建议过多使用beanshell这个插件,逻辑比较复杂,或者代码量比较大,建议开发自定义插件方式,把逻辑在ideal写好弄成一个jar包,放在jmeter下的Lib的ext目录下。
在java写里也是添加了相关的依赖,最终暴露出来一个对应协议的接口。通过java来写,即就是将相关依赖添加进去即好。
有些协议没有相关的插件,就只能自己写。
可以自定义没有的函数,即使函数助手有很多函数,可能也有一些不能满足需求,可以开发函数。
二、自定义请求编写(Java Sampler)
1、第一步:创建maven项目
演示
先来创建一个maven项目
再确认maven的全局配置
2、第二步:添加依赖(ApacheJMeter_core、ApacheJMeter_java)
maven库:https://mvnrepository.com/
直接去maven仓库https://mvnrepository.com/搜索ApacheJMeter_core、ApacheJMeter_java
①添加ApacheJMeter_core依赖
此处有不同版本的依赖,此处选择和jmeter5.1.1版本一样的版本。
点进去后有maven的坐标
复制粘贴到jmeter的pom.xml文件中
需再加一个dependencies坐标
验证是否下载好
②添加ApacheJMeter_java依赖
复制粘贴到jmeter的pom.xml文件中
验证是否下载好
直接去maven仓库https://mvnrepository.com/搜索ApacheJMeter_core、ApacheJMeter_java
搜索出来后,根据Jmeter版本选择,在pom添加依赖,依赖下载后可在右侧maven确认是否下载好。
若没有外网,先把jar包下载下来,放到本地maven仓库,但是需要按照dependency中的坐标创建目录。
本地maven仓库C:\ZTools\maven,再根据pom里dependency中的groupid来找
3、 第三步:实现 Java Sampler 功能的两种方式
二次开发(实现 Java Sampler 功能)有两种方式:实现 JavaSamplerClient 接口、继承 AbstractJavaSamplerClient 抽象类
3.1、先看一下JavaSamplerClient 接口源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.jmeter.protocol.java.sampler;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.samplers.SampleResult;
public interface JavaSamplerClient {
void setupTest(JavaSamplerContext var1);
SampleResult runTest(JavaSamplerContext var1);
void teardownTest(JavaSamplerContext var1);
Arguments getDefaultParameters();
}
这个接口有四个抽象方法
第一个方法是做初始化的。
第二个是返回SampleResult类型对象,runTest是实现逻辑功能的主方法,每个线程都会循环执行此方法。
第三个是做一些收尾的工作。
第四个是获取默认参数以及默认值获取到后会在jmeter的gui展示,类似以下这种key,value方式展示
3.2、验证四个方法的执行顺序
写代码验证一下执行顺序
package com.zhou.demo;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
public class JmeterDemo implements JavaSamplerClient {
public void setupTest(JavaSamplerContext javaSamplerContext) {
System.out.println("=======setupTest=============");
}
public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
System.out.println("=======runTest=============");
return null;
}
public void teardownTest(JavaSamplerContext javaSamplerContext) {
System.out.println("=======teardownTest=============");
}
public Arguments getDefaultParameters() {
System.out.println("=======getDefaultParameters=============");
Arguments args = new Arguments();
args.addArgument("usename","tester");
return args;
}
}
运行后的执行顺序
四个方法的执行顺序是:getDefaultParameters–setupTest–runTest–teardownTest
①第一种:实现 JavaSamplerClient 接口
如果一个类抽象类要么去实现一个接口,要么实现接口中的方法。
选中JavaSamplerClient alt+enter选择 Implements Methods
第一个方法setupTest(初始化)
teardownTest(收尾工作)最后运行
getDefaultParameters在gui显示属性或属性值,去创建JavaSampler时就会调用这个方法,把对应的属性或默认值显示在gui界面上。
类名称有两个:org.apache.jmeter.protocol.java.test.JavaTest
和org.apache.jmeter.protocol.java.test.SleepTest
在idea中看一下JavaTest类是怎么编写的,Ctrl+n
JavaTest这个类是通过继承抽象类AbstractJavaSamplerClient 实现
在idea中看一下JavaTest类是怎么编写的,Ctrl+n
继承抽象类的方式
runTest实现功能逻辑都写这里面。
顺序是getDefaultParameter、setupTest、runTest、teardownTest
②第二种:继承 AbstractJavaSamplerClient 抽象类
两者主要区别:实现接口的话,要重写四个方法
,继承抽象类的话,不想重写四个方法,建议用继承抽象类的方式。
org.apache.jmeter.protocol.java.test.JavaTest
org.apache.jmeter.protocol.java.test.SleepTest这两个类名称都是去继承 AbstractJavaSamplerClient 抽象类
演示:
此处有一个查看上传文件接口
先将图片保存到本地的逻辑实现一下,此处要用到httpclient
添加httpclient依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
将保存到本地的逻辑封装在一个方法中
写一个静态方法getPic,
保存图片需要先获取图片,再保存到具体的目录。此处需要先定义两个形参(图片的url,图片保存的路径)
请求是一个get请求,所以此处需要封装一个get请求对象
httpget的构造方法有三个
此处用第二个构造方法,将url传进去
创建一个客户端
创建客户端后就去发送请求,通过httpclient发送请求
httpclient.execute,将其httpget请求传进来
response就是发送请求获取的结果
类型是CloseableHttpResponse
此处结果需要将其转成字节数组
下面就将内容写到本地
创建文件字节输出流,保存的是图片,创建文件字节输出对象
new FileOutputStream,构造方法选String,将文件的路径传进来
传文件路径还需要进行拼接
上面getPisc方法只传了目录
目录拼接上文件名,就是文件的完整路径
就还需要将url相当于做一个分割提取
即就是get http://
将文件名提取出来
此处通过/作为分隔符
url.split),此处获取的就是一个数组
文件名是最后一个元素,可以定义一个变量filename,索引能不能写-1
此处先要获取数组的长度.length
最后一个索引就是lenth-1,获取到后再进行拼接
创建文件字节输出流之后,可以进行调用write方法
write方法也是重载
可以传一个字节数组
out.write
获取到的字节数组传进来
out。write(bytes)
关闭流对象
上面保存图片的逻辑完成
下面完成继承abstractjavasampleClient抽象类的逻辑
继承抽象类
abstractjavasampleClient这个抽象类没有实现runtest方法
就需要去实现这个方法
这个方法返回是SampleResult类型的方法。就需要去创建这个类型的对象
获取参数url,dir
通过javasampleContent.getParameter()
保存图片到本地
不是在同一个类中,调静态方法就必须加类名
调方法,传url,dir
想要的是在响应中不展示图片,而是改为xx文件保存成功,请在xx路径下查看
就需要将文件名获取到
就需要在url中提取到
此处需要做一个分割
提取文件名
url
调保存图片到本地就需要去获取响应数据
sampleResult.setResponData 此处就设置一个字符串
查看结果树要显示某某文件保存成功
sampleResult.setResponData (filename+保存成功,请到路径查看)
还有一个响应编码,可以直接传一个null,默认就是utf8
设置成功
sampleResult.setSuccessful (true)
以上runtest就写好
展示默认参数名和值
还需要重写getdefaultParameters
此处返回一个Arguments对象
将默认的参数名和值写上,第一个是url,第二个参数是dir
此处就写完
下一步就是打包
将打包好的jar包放在jmeter的lib/ext目录下,重启jmeter
添加Java请求
路径改一下test2,考虑一下异常
刚自定义java请求已经ok
此处有一个问题,每次代码写好后,都需要去通过打包后放到lb下进行测试,比较麻烦
能不能直接在我们的类文件中来测试
这是可以的
此处写一个main方法
把调用runtest方法,runtest方法是非静态的
得先去创建一个对象,再去调runtest方法
此处的runtest方法需要传一个参数,他是JavasampleContext类型,此处就需要new一个类型的对象
来看一下JavasampleContext构造方法,此处要传一个Arguments类型的
在这个类里是重写了getdefaultParameters方法,返回的类型就是Argunments
此处就直接obj.getDefaultParameters
他就是返回Argunments类型的对象
再去传
此处就获取到JavasampleContext类型的对象
就将samplecontext对象传到runtest方法中
此处是主要来测试润test的逻辑是否ok
直接调runtest方法就可以了
为了方便,加一个打印
运行,保存成功,此处通过写一个测试方法,测试一下,不然每次打一个jar包,放jmeter会比较麻烦
前面都是固定写法
主要是固定保存图片到本地写法处
写一个测试dubbo协议的逻辑,就可以写在这里
GetPics类
package com.zhou.demo;
import com.mongodb.DBPortPool;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
//保存图片业务逻辑代码
public class GetPics {
public static boolean GetPic(String url, String dir) {
// url= "http://47.108.153.47:18089/qzcsbj/test1.png";
// dir="D:/aaa/";
String[] arrs = url.split("/");
String fname = arrs[arrs.length - 1];
//1.封装get请求对象
HttpGet httpGet = new HttpGet(url);
//2.创建一个客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
//3.发送请求
FileOutputStream fos = null;
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
System.out.println(""+response);
if (response.toString().contains("HTTP/1.1 404")){
System.out.println("请求的文件不存在");
return false;
}
//4.将CloseableHttpResponseresponse类型的response对象结果转成字节数组
byte[] bytes = EntityUtils.toByteArray(response.getEntity());
System.out.println("===========response=============="+response);
//5.创建输出字节流对象
fos = new FileOutputStream(dir + fname);
//6.写文件
fos.write(bytes);
System.out.println("完成");
} catch (Exception e) {
System.out.println("===========文件下载异常============");
e.printStackTrace();
} finally {
//7.关闭流
if (fos != null) {
try {
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return true;
}
}
JmeterPics类
package com.zhou.demo;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
public class JmeterPics extends AbstractJavaSamplerClient {
public static void main(String[] args) {
JmeterPics obj = new JmeterPics();
JavaSamplerContext javaSamplerContext = new JavaSamplerContext(obj.getDefaultParameters());
obj.runTest(javaSamplerContext);
}
public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
//1.方法需要一个 SampleResult类型的对象,即先new一个
SampleResult sampleResult = new SampleResult();
//2.获取参数
String url = javaSamplerContext.getParameter("url");
String dir = javaSamplerContext.getParameter("dir");
//4.提取文件名
String[] args = url.split("/");
String fname = args[args.length - 1];
//3.保存图片到本地
boolean flag = GetPics.GetPic(url, dir);
sampleResult.setSuccessful(true);
if (flag) {
sampleResult.setResponseData(fname + "保存成功,请在 " + dir + "下查看");
} else {
sampleResult.setResponseData(fname + "请求的文件不存在");
}
return sampleResult;
}
@Override
public Arguments getDefaultParameters() {
Arguments argument = new Arguments();
argument.addArgument("url", "http://47.108.153.47:18089/qzcsbj/test.png");
argument.addArgument("dir", "D://");
return argument;
}
}
4.打包
先clean后再Package
打包后会在idea中生成target
将自己打的jar包复制在jmeter下的ext目录下。
点住jar包-右键-show in Explorer
target下就会有这个jar包
复制放到jmeter下的ext的lib目录下
重启Jmeter
把自己写的类读取到
三、自定义函数(了解)
jmeter函数助手提供了很多常用的功能,但是有时也不能满足我们的需求,我们可以写beanshell实现,对于经常使用的功能,也可以自己写一个函数方便我们使用
如果无从着手,可以下载jmeter源码,参考自带函数的写法
5.1版本的路径为:apache-jmeter-5.1.1\src\functions\org\apache\jmeter\functions
参考:Uuid、Random
几个实现方法的执行顺序是:getReferenceKey–getArgumentDesc–setParameters–execute
getReferenceKey:函数助手对话框中的下拉框中显示的函数名称
getArgumentDesc:参数描述,有几个参数,就几个描述
setParameters:接收用户输入的参数值,放collection中
execute:根据入参,执行核心逻辑,保存结果至相应的变量中
添加pom依赖:ApacheJMeter_functions
提供函数助手开发相关的类
新建类
新建包:名字只能是 functions 结尾
继承抽象类:AbstractFunction