一文搞懂JMeter engine中HashTree的配置问题

网友投稿 915 2022-12-11

一文搞懂JMeter engine中HashTree的配置问题

一文搞懂JMeter engine中HashTree的配置问题

目录一、前言二、HashTree的用法三、JMeter源码导出jmx脚本文件介绍四、自定义HashTree生成JMeter脚本

一、前言

之前介绍了JMeter engine启动原理,但是里面涉及到HashTree这个类结构没有给大家详细介绍,这边文章就详细介绍JMeter engine里面的HashTree结构具体用来做什么

大家看到下面是JMeter控制台配置截图,是一个标准的菜单形式;菜单形式其实就类似于“树型”的数据结构,而HashTree其实就是一个树型数据结构

我们在JMeter控制台导出的jmx文件,是一个xml结构的数据,他其实就是由HashTree生成的,后面我们会讲到

二、HashTree的用法

首先通过HashTree类介绍,它一个集合类;具备Map结构的功能,而且是一种树型结构

/**

* This class is used to create a tree structure of objects. Each element in the

* tree is also a key to the next node down in the tree. It provides many ways

* to add objects and branches, as well as many ways to retrieve.

*

* HashTree implements the Map interface for convenieALVIFHTnce reasons. The main

* difference between a Map and a HashTree is that the HashTree organizes the

* data into a recursive tree structure, and provides the means to manipulate

* that structure.

*

* Of special interest is the {@link #traverse(HashTreeTraverser)} method, which

* provides an expedient way to traverse any HashTree by implementing the

* {@link HashTreeTraverser} interface in order to perform some operation on the

* tree, or to extract information from the tree.

*

*http:// @see HashTreeTraverser

* @see SearchByClass

*/

public class HashTree implements Serializable, Map, Cloneable {

}

JMeter常用的HashTree方法(以下图配置为例)

//ListedHashTree是HashTree的继承类,可以保证HashTree的顺序性

HashTree tree = new ListedHashTree();

//TestPlan对象,测试计划

TestPlan plan = new TestPlan();

//ThreadGroup对象,线程组

ThreadGroup group = new ThreadGroup();

//创建线程组数结构的对象groupTree

HashTree groupTree = new ListedHashTree();

//表示取样器中的HTTP请求

HTTPSamplerProxy sampler = new HTTPSamplerProxy();

//创建HTTP请求的数结构对象samplerTree

//调用put方法相当于在plan(测试计划)菜单对象下添加group(线程组)子菜单,这样就形成了一种树型结构

HashTree samplerTree = new ListedHashTree();

samplerTree.put(sampler,new ListedHashTree())

//groupTree树结构添加子树samplerTree

groupTree.put(group,samplerTree)

//tree树结构为测试计划对象,添加子树groupTree,这样就形成了上图的层级形式

tree.put(plan, groupTree)

//调用add方法相当于在tree菜单对象下添加同级菜单

tree.add(Object key)

三、JMeter源码导出jmx脚本文件介绍

首先在JMeter控制台所有点击事件,都会被ActionRouter中performaAction方法进行监听执行,点击导出按钮,会进入到如图方法通过反射由Save类执行

在Save类中执行doAction主要是获取到配置的HashTree

当你点击保存的时候,它会创建一个空文件,此时文件没有任何内容

类的doAction方法最后会调用backupAndSave(e, subTree, fullSave, updateFile)这个是来将创建的空文件写入xml内容的

在SaveService中saveTree方法,其中JMXSAVER是XStream对象,对应的maven坐标如下

com.thoughtworks.xstream

xstream

1.4.15

四、自定义HashTree生成JMeter脚本

首先maven引入以下几个坐标5.3

org.apache.jmeter

ApacheJMeter_http

${jmeter.version}

org.apache.logging.log4j

log4j-slf4j-impl

org.apache.jmeter

ApacheJMeter_functions

${jmeter.version}

org.apache.jmeter

ApacheJMeter_jdbc

${jmeter.version}

org.apache.jmeter

ApacheJMeter_tcp

${jmeter.version}

先创建一个取样器,然后写成HashTree的数据结构

public static ThreadGroup threadGroup;

//创建一个标准的线程组

private static void initThreadGroup(){

LoopController loopController = new LoopController();

loopController.setName("LoopController");

loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());

loopController.setProperty(TestElement.GUI_CLASS, JMeterUtil.readSaveProperties("LoopControlPanel"));

loopController.setEnabled(true);

loopController.setLoops(1);

ThreadGroup group = new ThreadGroup();

group.setEnabled(true);

group.setName("ThreadGroup");

group.setProperty(TestElement.TEST_CLASS, JMeterUtil.readSaveProperties("ThreadGroup"));

group.setProperty(TestElement.GUI_CLASS, JMeterUtil.readSaveProperties("ThreadGroupGui"));

group.setProperty(ThreadGroup.ON_SAMPLE_ERROR,"continue");

group.setProperty(ThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION,true);

group.setProperty(TestElement.COMMENTS,"");

group.setNumThreads(1);

group.setRampUp(1);

group.setDelay(0);

group.setDuration(0);

group.setProperty(ThreadGroup.ON_SAMPLE_ERROR, ThreadGroup.ON_SAMPLE_ERROR_CONTINUE);

group.setScheduler(false);

group.setSamplerController(loopController);

threadGroup = group;

}

创建一个标准的线程组

public static ThreadGroup threadGroup;

//创建一个标准的线程组

private static void initThreadGroup(){

LoopController loopController = new LoopController();

loopController.setName("LoopController");

loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());

loopController.setProperty(TestElement.GUI_CLASS, JMeterUtil.readSaveProperties("LoopControlPanel"));

loopController.setEnabled(true);

loopController.setLoops(1);

ThreadGroup group = new ThreadGroup();

group.setEnabled(true);

group.setName("ThreadGroup");

group.setProperty(TestElement.TEST_CLASS, JMeterUtil.readSaveProperties("ThreadGroup"));

group.setProperty(TestElement.GUI_CLASS, JMeterUtil.readSaveProperties("ThreadGroupGui"));

group.setProperty(ThreadGroup.ON_SAMPLE_ERROR,"continue");

group.setProperty(ThreadGroup.IS_SAME_USER_ON_NEXT_ITERATION,true);

group.setProperty(TestElement.COMMENTS,"");

group.setNumThreads(1);

group.setRampUp(1);

group.setDelay(0);

group.setDuration(0);

group.setProperty(ThreadGroup.ON_SAMPLE_ERROR, ThreadGroup.ON_SAMPLE_ERROR_CONTINUE);

group.setScheduler(false);

group.setSamplerController(loopController);

threadGroup = group;

}

创建一个标准的测试计划

public static TestPlan testPlan;

//创建一个标准的测试计划

private static void initTestPlan() {

TestPlan plan = new TestPlan();

//设置测试计划属性及内容,最后都会转为xml标签的属性及内容

plan.setProperty(TestElement.NAME, "测试计划");

plan.setProperty(TestElement.TEST_CLASS, JMeterUtil.readSaveProperties("TestPlan"));

plan.setProperty(TestElement.GUI_CLASS, JMeterUtil.readSaveProperties("TestPlanGui"));

plan.setEnabled(true);

plan.setComment("");

plan.setFunctionalMode(false);

plan.setTearDownOnShutdown(true);

plan.setSerialized(false);

plan.setProperty("TestPlan.user_define_classpath","");

plan.setProperty("TestPlan.user_defined_variables","");

plan.setUserDefinedVariables(new Arguments());

testPlan = plan;

}

开始封装成一个HashTree的配置

//先创建一个测试计划hashtree对象

HashTree hashTree = new ListedHashTree();

//在创建一个线程组threaddGroupTree对象

HashTree threadGroupTree = new ListedHashTree();

//HttpRequestConfig为HTTP对应的请求头、请求体等信息数据,传入httpToHashTree静态方法获取到取样器的HashTree数据结构,源码上图已分享

HashTree httpConfigTree = XXClass.httpToHashTree(HttpRequestConfig httpRequestData)

//threadGroupTree添加子菜单httpConfigTree对象

threadGroupTree.put(group, httpConfigTree);

//测试计划hashTree添加子菜单threadGroupTree对象

hashTree.put(JMeterTestPlanConfigService.testPlan, threadGroupTree);

HashTree写好后,调用JMeter原生方法SaveService.saveTree(hashTree,outStream);生成对应的xml

如果直接调用的话生成的xml格式会形成如下图所示,而非JMeter原生导出jmx形式,这种文件结构JMeter控制台读取会报错,识别不了

后面阅读SaveService源码才明白,生成xml文件之前会先初始化静态代码块内容,初始化属性

过程中会调用JMeterUtils中的findFile方法来寻找saveservice.properties文件

由于SaveService 中都是静态方法无法重写,所以根据最后调用JMeterUtils中的findFile方法来寻找saveservice.properties有两种解决方案

方案一 :不推荐,在项目根目录下存放saveservice.properties,这样findFile方法就能拿到,但是这样不好,因为maven打包的时候该文件会打不进去,至少我springboot项目是遇到这样的问题

方案二:推荐,创建一个临时文件命名为saveservice.properties,然后提前将saveservice.properties配置读取到临时文件中,这样在调用JMeterUtils中的findFile方法同样能够找到配置,成功解决SaveService初始化属性导致的问题,具体代码如下

private void hashTreeToXML(HashTree hashTree,PressureConfigInfo configInfo){

FileOutputStream outStream = null;

File file = new File("temp.jmx");

File tempFile = null;

try {

//创建一个临时的saveservice.properties文件

tempFile = new File("saveservice.properties");

InputStream is = JMeterUtil.class.getResource("/jmeter/saveservice.properties").openStream();

//将配置文件写入临时文件中

FileUtil.writeFromStream(is,tempFile);

outStream = new FileOutputStream(file);

//调用saveTree成功转为xml

SaveService.saveTree(hashTree,outStream);

String xmlContent = FileUtil.readUtf8String(file);

configInfo.setFile(xmlContent.getBytes());

} catch (IOException e) {

e.printStackTrace();

}finally {

try {

FileUtils.forceDelete(file);

FileUtils.forceDelete(tempFile);

} catch (IOException e) {

e.printStackTrace();

}

}

}

最后生成的xml文件结构如下图,通过JMeter控制台也能成功打开识别

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:vue和nvue(vue和nvue哪个好)
下一篇:浅谈JMeter engine启动原理
相关文章

 发表评论

暂时没有评论,来抢沙发吧~