前面针对jmx添加Backend Listener元件,现需要实现将Thread Group替换成Concurrency Thread Group,解决流量梯度发起的问题

一、jmx核心结构

1.1 Thread Group

<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="普通线程组" enabled="true"><stringProp name="ThreadGroup.on_sample_error">continue</stringProp><elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true"><boolProp name="LoopController.continue_forever">false</boolProp><stringProp name="LoopController.loops">1</stringProp></elementProp><stringProp name="ThreadGroup.num_threads">1</stringProp><stringProp name="ThreadGroup.ramp_time">1</stringProp><boolProp name="ThreadGroup.scheduler">false</boolProp><stringProp name="ThreadGroup.duration"></stringProp><stringProp name="ThreadGroup.delay"></stringProp><boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>

1.2 Concurrency Thread Group

<com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup guiclass="com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroupGui" testclass="com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup" testname="普通线程组" enabled="true"><elementProp name="ThreadGroup.main_controller" elementType="com.blazemeter.jmeter.control.VirtualUserController"/><stringProp name="ThreadGroup.on_sample_error">continue</stringProp><stringProp name="TargetLevel">2</stringProp><stringProp name="RampUp">12</stringProp><stringProp name="Steps">2</stringProp><stringProp name="Hold">1</stringProp><stringProp name="LogFilename"></stringProp><stringProp name="Unit">S</stringProp><stringProp name="Iterations"></stringProp>
</com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup>

二、加载修改属性

2.1 代码实现

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.apache.jmeter.threads.ThreadGroup;public class JmeterDocumentParser {static {// 设置Jmeter运行环境相关的配置String jmeterHome = System.getProperty("user.dir") + "/jmxs";String jmeterHomeBin = jmeterHome + "/bin";JMeterUtils.loadJMeterProperties(jmeterHomeBin + File.separator + "jmeter.properties");JMeterUtils.setJMeterHome(jmeterHome);JMeterUtils.initLocale();try {SaveService.loadProperties();} catch (IOException e) {e.printStackTrace();}}/*** 处理ConcurrencyThreadGroup线程组* @param jmxFilePath*/public void processConcurrencyThreadGroup(String jmxFilePath){// 加载jmx文件File in = new File(jmxFilePath);HashTree testPlanTree = SaveService.loadTree(in);// 遍历HashTree,如果为ThreadGroup类型则动态修改HashTree hashTree = testPlanTree.getTree(testPlanTree.keySet());for(Map.Entry<Object, HashTree> ele: hashTree.entrySet()){// 判断等同于ele.getKey().getClass().getName().equals("org.apache.jmeter.threads.ThreadGroup")if(ele.getKey() instanceof ThreadGroup){// 获取document对象ThreadGroup threadGroup = (ThreadGroup)ele.getKey();Document document = threadGroup.getOwnerDocument();// 修改guiclass属性值((Element) ele.getKey()).setAttribute("guiclass", CONCURRENCY_THREAD_GROUP + "Gui");}}}}

2.2 错误

  • 强转ThreadGroup类型

无法调用父类方法getOwnerDocument

Error:(202, 48) java: 找不到符号符号:   方法 getOwnerDocument()位置: 类型为org.apache.jmeter.threads.ThreadGroup的变量 threadGroup
  • 强转Element类型

执行报错无法转换

Exception in thread "main" java.lang.ClassCastException: org.apache.jmeter.threads.ThreadGroup cannot be cast to org.w3c.dom.Elementat com.lluozh.jmeter.JmxParse.JmeterDocumentParser.processConcurrencyThreadGroup(JmeterDocumentParser.java:200)at com.lluozh.jmeter.JmxParse.JmeterDocumentParser.main(JmeterDocumentParser.java:478)

无法直接转换数据类型并修改属性值,故该方式暂时不可行

三、加载Document对象

直接加载jmx文件,并解析为Document对象后再进行操作

3.1 ClassPathResource加载

  • 代码
public void getJmxElement(String jmxFilePath) throws IOException, ParserConfigurationException, SAXException {// 1、获取JMX资源InputStream in = new ClassPathResource(jmxFilePath).getInputStream();// 2、获取DocumentBuilderFactory实例DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();// 3、获取DocumentBuilder实例DocumentBuilder docBuilder = factory.newDocumentBuilder();// 4、将docBuilder转换为DocumentDocument doc = docBuilder.parse(in);// 5、获取节点并循环输出节点值Element element = doc.getDocumentElement();
}
  • 报错
Exception in thread "main" java.io.FileNotFoundException: class path resource [Users/lluozh/Desktop/lluozh.jmx] cannot be opened because it does not existat org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:172)at com.lluozh.jmeter.JmxParse.JmeterDocumentParser.lluozh(JmeterDocumentParser.java:452)at com.lluozh.jmeter.JmxParse.JmeterDocumentParser.main(JmeterDocumentParser.java:469)
  • 原因

文件在系统任意目录下,并不在target文件夹中,而classPathResource.getInputStream()是读编译后的文件,不是源代码文件,故报错文件不存在

3.2 SAXReader加载

  • 代码
import org.dom4j.Element;
import org.dom4j.Document;public void getJmxElement(String jmxFilePath) throws DocumentException {SAXReader reader = new SAXReader();Document document = reader.read(new File(jmxFilePath));Element bookStore = document.getRootElement();Element element = bookStore.element("hashTree").element("hashTree");element.getOwnerDocument();
}
  • 报错
Error:(455, 16) java: 找不到符号符号:   方法 getOwnerDocument()位置: 类型为org.dom4j.Element的变量 element

3.3 InputStream加载

  • 代码
public void getJmxElement(String jmxFilePath) throws IOException, ParserConfigurationException, SAXException {// 1、获取文件对象File file = new File(jmxFilePath);// 2、获取文件流InputStream input =  new FileInputStream(file);// 3、获取DocumentBuilderFactory实例DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();// 4、获取DocumentBuilder实例DocumentBuilder docBuilder = factory.newDocumentBuilder();// 5、将docBuilder转换为DocumentDocument doc = docBuilder.parse(input);
}
  • 数据获取

正常获取Document对象

四、处理数据

4.1 代码

public String parse(String jmxFilePath) throws IOException, ParserConfigurationException, SAXException, TransformerException, DocumentException {// 获取文件对象File file = new File(jmxFilePath);// 获取文件流InputStream input =  new FileInputStream(file);// 获取DocumentBuilderFactory实例DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();// 获取DocumentBuilder实例DocumentBuilder docBuilder = factory.newDocumentBuilder();// 将docBuilder转换为DocumentDocument doc = docBuilder.parse(input);// 遍历处理final Element jmeterTestPlan = doc.getDocumentElement();NodeList childNodes = jmeterTestPlan.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {Node node = childNodes.item(i);if (node instanceof Element) {Element ele = (Element) node;// jmeterTestPlan的子元素肯定是<hashTree></hashTree>parseHashTree(ele);}}return saveDocumentJmxFile(doc);}private void parseHashTree(Element hashTree){if (hashTree.getChildNodes().getLength() > 0) {final NodeList childNodes = hashTree.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {Node node = childNodes.item(i);if (node instanceof Element) {Element ele = (Element) node;if (nodeNameEquals(ele, THREAD_GROUP)) {// TODO 处理ThreadGroup}}}}
}

4.2 问题

在遍历时发现并未匹配到THREAD_GROUP

是获取Document数据的问题呢还是数据获取的层级不对?

  • Document数据


通过网上搜索资料和比对数据发现:

  1. 实例本身其实并不为null,对于数据的处理并没有任何影响
  2. 在执行解析操作之后,所有非静态对象都只是引用了null,将其设置为静态会强制Java在变量的解析操作期间保留对创建对象的引用
  • 数据层级

通过对Document数据解析和比对jmx数据文件,其实THREAD_GROUP的层级为:

doc.getChildNodes().item(0).getChildNodes().item(1).getChildNodes().item(3).getChildNodes().item(1)

代码中遍历少了一层,需要递归遍历获取THREAD_GROUP

4.3 修复

private final static String HASH_TREE_ELEMENT = "hashTree";
private final static String THREAD_GROUP = "ThreadGroup";private void parseHashTree(Element hashTree){if (hashTree.getChildNodes().getLength() > 0) {final NodeList childNodes = hashTree.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {Node node = childNodes.item(i);if (node instanceof Element) {Element ele = (Element) node;if (nodeNameEquals(ele, HASH_TREE_ELEMENT)) {parseHashTree(ele);}else if (nodeNameEquals(ele, THREAD_GROUP)) {// TODO 处理ThreadGroup}}}}}

增加判断如果类型hashTree时递归遍历,更新后可正常遍历获取

五、ThreadGroup处理

5.1 处理ThreadGroup

private void processThreadGroup(Element threadGroup) {// 检查 threadgroup 后面的hashtree是否为空Node hashTree = threadGroup.getNextSibling();while (!(hashTree instanceof Element)) {hashTree = hashTree.getNextSibling();}if (!hashTree.hasChildNodes()) {MSException.throwException(Translator.get("jmx_content_valid"));}// 重命名 tagNameDocument document = threadGroup.getOwnerDocument();document.renameNode(threadGroup, threadGroup.getNamespaceURI(), CONCURRENCY_THREAD_GROUP);threadGroup.setAttribute("guiclass", CONCURRENCY_THREAD_GROUP + "Gui");threadGroup.setAttribute("testclass", CONCURRENCY_THREAD_GROUP);/*<elementProp name="ThreadGroup.main_controller" elementType="com.blazemeter.jmeter.control.VirtualUserController"/><stringProp name="ThreadGroup.on_sample_error">continue</stringProp><stringProp name="TargetLevel">2</stringProp><stringProp name="RampUp">12</stringProp><stringProp name="Steps">2</stringProp><stringProp name="Hold">3</stringProp><stringProp name="LogFilename"></stringProp><stringProp name="Iterations">1</stringProp><stringProp name="Unit">S</stringProp>*/removeChildren(threadGroup);// elementPropElement elementProp = document.createElement("elementProp");elementProp.setAttribute("name", "ThreadGroup.main_controller");elementProp.setAttribute("elementType", "com.blazemeter.jmeter.control.VirtualUserController");threadGroup.appendChild(elementProp);threadGroup.appendChild(createStringProp(document, "ThreadGroup.on_sample_error", "continue"));threadGroup.appendChild(createStringProp(document, "TargetLevel", "2"));threadGroup.appendChild(createStringProp(document, "RampUp", "12"));threadGroup.appendChild(createStringProp(document, "Steps", "2"));threadGroup.appendChild(createStringProp(document, "Hold", "1"));threadGroup.appendChild(createStringProp(document, "LogFilename", ""));// bzm - Concurrency Thread Group "Thread Iterations Limit:" 设置为空// threadGroup.appendChild(createStringProp(document, "Iterations", "1"));// threadGroup.appendChild(createStringProp(document, "Unit", "M"));// 单位改成秒threadGroup.appendChild(createStringProp(document, "Unit", "S"));}

5.2 处理ConcurrencyThreadGroup

 private void processConcurrencyThreadGroup(Element concurrencyThreadGroup) {String testname = concurrencyThreadGroup.getAttribute("testname");concurrencyThreadGroup.setAttribute("testname", testname + "-0");if (concurrencyThreadGroup.getChildNodes().getLength() > 0) {final NodeList childNodes = concurrencyThreadGroup.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {Node node = childNodes.item(i);if (node instanceof Element) {Element ele = (Element) node;if (invalid(ele)) {continue;}if (nodeNameEquals(ele, STRING_PROP)) {parseStringProp(ele);}}}}}

5.3 修改parseHashTree

增加ThreadGroup和ConcurrencyThreadGroup的处理

private void parseHashTree(Element hashTree){if (hashTree.getChildNodes().getLength() > 0) {final NodeList childNodes = hashTree.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {Node node = childNodes.item(i);if (node instanceof Element) {Element ele = (Element) node;if (nodeNameEquals(ele, HASH_TREE_ELEMENT)) {parseHashTree(ele);}else if (nodeNameEquals(ele, THREAD_GROUP)) {processThreadGroup(ele);processConcurrencyThreadGroup(ele);}}}}
}

5.4 其他函数

/***
* 移除所有的子节点
* @param node
*/
private void removeChildren(Node node) {while (node.hasChildNodes()) {node.removeChild(node.getFirstChild());}
}/***
* 创建StringProp
*/
private Element createStringProp(Document document, String name, String value) {Element unit = document.createElement(STRING_PROP);unit.setAttribute("name", name);unit.appendChild(document.createTextNode(value));return unit;
}/***
* 判断节点名称
*/
private boolean nodeNameEquals(Node node, String desiredName) {return desiredName.equals(node.getNodeName()) || desiredName.equals(node.getLocalName());
}/***
* 判断属性是否存在
*/
private boolean invalid(Element ele) {return !StringUtils.isBlank(ele.getAttribute("enabled")) && !Boolean.parseBoolean(ele.getAttribute("enabled"));
}

六、保存jmx文件

需要将修改后的Document数据对象保存成jmx文件

private void saveDocumentJmxFile(Document document) throws TransformerException {DOMSource domSource = new DOMSource(document);StringWriter writer = new StringWriter();StreamResult result = new StreamResult(writer);TransformerFactory tf = TransformerFactory.newInstance();Transformer transformer = tf.newTransformer();transformer.transform(domSource, result);// 保存文件transformer.transform(new DOMSource(document), new StreamResult(new File("/Users/lluozh/Desktop/lluozh202003032030.jmx")));
}

执行后可生成正确的jmx文件

【Furion】动态修改jmx的ConcurrencyThreadGroup元件相关推荐

  1. SpringBoot动态修改日志级别

    前言 为了减少日志频繁打印带来的性能影响,线上环境设置的日志级别一般都相对较高.而当出现生产问题需要排查的时候,可能需要适当降低日志级别(例如DEBUG)来打印更多的日志信息帮助定位问题. 传统的做法 ...

  2. uefi下的开机顺序_动态修改UEFI启动顺序的方法与流程

    本发明涉及计算机领域,其主要 技术实现要素: 是动态修改UEFI启动顺序的方法. 背景技术: 固件是固化在Flash芯片中的软件程序.BIOS是计算机中最重要的固件之一,用于初始化硬件.管理硬件资源. ...

  3. mysql中如何设置过滤器_mysql 如何动态修改复制过滤器

    MySQL动态修改复制过滤器 说说今天遇到的问题吧,今天在处理一个业务方的需求,比较变态,我大概描述一下: 1.线上的阿里云rds上面有个游戏的日志库,里面的表都是日表的形式,数据量比较大了,每次备份 ...

  4. js动态变量名_scss引入其他scss变量,并通过js动态修改scss变量

    a.scss .test{ background:red;} 1 2 3 b.scss $bgColor: red; 1 1.a.scss需要引入b.scss中的变量 a.scss(只要引入成功后,修 ...

  5. java.lang.Instrument 动态修改替换类代码

    java.lang.Instrument 动态修改替换类代码 | java.lang.Instrument包是在JDK5引入的,程序员通过修改方法的字节码实现动态修改类代码. 这通常是在类的main方 ...

  6. SAP QM中阶之动态修改规则创建

    SAP QM中阶之动态修改规则创建 1, 执行事务代码QDR1或者如下的菜单路径, 可以用来创建动态修改规则. 点击菜单或者执行事务代码QDR1, 进入如下界面, 输入DMR的code,比如Z01,回 ...

  7. SAP QM Dynamic Modification Rule (动态修改规则)

    SAP QM Dynamic Modification Rule (动态修改规则) 我们讲到决定样本大小的方式具体有三种: ⭐手动输入 ⭐比例大小 ⭐采样过程 然后采样过程的创建通常也是有两种方式: ...

  8. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

    原文:利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...

  9. linux 软件集成工具箱,在PB中动态修改SQL语句

    在PB中动态修改SQL语句 分享到: 江苏省南通电信局网管中心 黄莹 ---- PowerBuilder是图形界面的Client/Server应用程序开发环境,可以很容易开发出功能强大的应用程序,在当 ...

最新文章

  1. R语言ggplot2可视化:使用gganimate包和gapminder包为生成的动画文件gif设置尺寸、分辨率
  2. Java Socket编程 - 基于TCP方式的二进制文件传输
  3. 【随笔】如何快速转载CSDN中的博客
  4. 无法连接上 cn.archive.ubuntu.com:80 (123.129.214.98)。 - connect (111: 拒绝连接)
  5. 不能为虚拟电脑 ubuntu 打开一个新任务的解决办法
  6. hdu 4911 “Inversion”——逆序对问题
  7. WIN7系统设置保护视力的豆沙绿过程
  8. 什么是PING值,PING值的计算方法
  9. 2021.11.08 - 143.猜数字游戏
  10. unity projector相关探讨和使用
  11. Statement接口的基本介绍和使用
  12. ROS IDE - RoboWare Studio 安装与使用教程
  13. 数论读书笔记——欧几里得和扩展欧几里得
  14. 【震惊】没有java环境也能运行jar,在不安装jdk下如何运行jar包
  15. 是否可以用不同版本的Flutter版本打包在iOS上实现马甲包
  16. Qt开发必备技术栈学习路线和资料
  17. Python入门:Python基础
  18. 简易人事管理系统(pyqt5+mysql)
  19. 蓝领公寓3期故障处理
  20. 孕妇写真居然可以这样拍

热门文章

  1. Vue中methods、computed和watch属性联系及区别
  2. 3个最佳的视频播放器
  3. 三年级神奇电子计算机教案,三年级信息技术计算机教案全册
  4. 如何帮助抑制高频干扰?
  5. java中Arrays详解
  6. 如何在Google Chromebook上更改墙纸和主题
  7. 如何在VMware 虚拟机下Linux查看IP(从而远程连接)
  8. ajax stalled,nutz后台访问页面的时候,出现浏览器出现一个Stalled状态,等待6s多,才会执行请求...
  9. 荣耀3C LTE(4G) set_immutable.list删除 VIPER4Android安装
  10. [机缘参悟-85]:读《价值 - 张磊》有感