Jmeter在Java中压测并使用Java对压测结果进行收集

Spring Wu 475 2021-08-17

目标

  • 1.需要利用jmeter做一个压测平台,读取我们自定义的jmx文件模板,把用户传入的数据将文件内容中的${requestBody}或者其他的一些自定义模板变量进行替换。
  • 2.将jmx文件丢给jmeter引擎执行
  • 3.收集压测结果

如图:
示例

引入Maven

            <dependency>
                <groupId>org.apache.jmeter</groupId>
                <artifactId>ApacheJMeter_core</artifactId>
                <version>${jmeter.version}</version>
            </dependency>
            <!--jMeter component-->
            <dependency>
                <groupId>org.apache.jmeter</groupId>
                <artifactId>ApacheJMeter_components</artifactId>
                <version>${jmeter.version}</version>
            </dependency>
            <!--jMeter Http包-->
            <dependency>
                <groupId>org.apache.jmeter</groupId>
                <artifactId>ApacheJMeter_http</artifactId>
                <version>${jmeter.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.jmeter</groupId>
                <artifactId>ApacheJMeter_functions</artifactId>
                <version>${jmeter.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.jmeter</groupId>
                <artifactId>ApacheJMeter_jdbc</artifactId>
                <version>${jmeter.version}</version>
            </dependency>

            <dependency>
                <groupId>org.apache.jmeter</groupId>
                <artifactId>ApacheJMeter_tcp</artifactId>
                <version>${jmeter.version}</version>
            </dependency>

截止发文,当前最新版本的jmeter是5.4.1

思路

  • 1.初始化jmeter
  • 2.读取自定义的jmx模板
  • 3.copy一份jmx模板,不能改变模板内容,只能在复制体上操作,因为模板还要再继续使用的
  • 4.对复制体内容进行替换
  • 5.更改复制体文件后缀为jmx,jmeter引擎只能接受jmx后缀的文件
  • 6.将jmx文件丢给jmeter使用
  • 7.收集压测结果
public void testReferenceLocal() {
	 /*********** 1.jmeter 初始化 ************/
        // jemter 引擎
        StandardJMeterEngine standardJMeterEngine = new StandardJMeterEngine();
        // 设置不适用gui的方式调用jmeter
        System.setProperty(JMeter.JMETER_NON_GUI, "true");
        JMeterUtils.loadJMeterProperties(jMeter_home_path + "/bin/jmeter.properties");
        JMeterUtils.setJMeterHome(jMeter_home_path);
        JMeterUtils.initLocale();
        File tempFile = null;
        File jmxFile = null;
        try {
            SaveService.loadProperties();

            /*********** 2.对模板文件进行操作 ************/			
            // 模板文件位置 该文件是一个jmx文件
            String templateFilePath = "/Users/wushuaiping/apache-jmeter-5.4.1/bin/HTTP请求.ftl";
            // copy文件
            File templateFile = new File(templateFilePath);
            String templateFileName = templateFile.getName();
            // 真实环境需要考虑文件重复
            tempFile = File.createTempFile(templateFileName.substring(0, templateFileName.lastIndexOf(".")) + "-userId-" + System.currentTimeMillis(), ".ftl");
            // 真实环境中可以做缓存,将该临时文件缓存起来,这样就不用每次就去copy操作了消耗机器性能。
            FileUtils.copyFile(templateFile, tempFile);

            /*********** 3.对copy后的文件进行内容替换 ************/
            Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
            configuration.setDefaultEncoding("utf-8");
            String tempFilePath = tempFile.getPath();
            configuration.setDirectoryForTemplateLoading(new File(tempFilePath.substring(0, tempFilePath.lastIndexOf(File.separator) + 1)));
            Template template = configuration.getTemplate(tempFile.getName(), "utf-8");
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tempFile),
                    StandardCharsets.UTF_8), 10240);
            Map<String, String> dataMap = new HashMap<>();
            Map<String, String> user = new HashMap<>();
            user.put("name", "Wsp");
            user.put("age", "18");
            dataMap.put("requestBody", StringEscapeUtils.escapeXml(JSON.toJSONString(user)));
            template.process(dataMap, out);
            out.close();

            /*********** 4.内容替换完成,更改文件后缀为jmx,丢给jmeter执行 ************/
            String jmxSuffixTempFile = tempFile.getPath().substring(0, tempFile.getPath().lastIndexOf(".")) + ".jmx";
            jmxFile = new File(jmxSuffixTempFile);
            tempFile.renameTo(jmxFile);
            String fileToString = FileUtils.readFileToString(tempFile, "utf-8");
            TemplateUtil.render(fileToString, dataMap);
            // 使用jmeter进行压测
            HashTree testPlanTree = SaveService.loadTree(jmxFile);
            //4.create Summariser.java
            Summariser summer = null;
            String summariserName = JMeterUtils.getPropDefault("summariser.name", "summary");
            if (summariserName.length() > 0) {
                summer = new Summariser(summariserName);
            }
            ResultCollector resultCollector = new ResultCollector(summer);
            testPlanTree.add(testPlanTree.getArray(), resultCollector);
            standardJMeterEngine.configure(testPlanTree);
            standardJMeterEngine.run();

            /*********** 5.获取压测结果 ************/
            SummariserRunningSample total = summer.getTotal();
            System.out.println(total);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (tempFile != null) {
                tempFile.deleteOnExit();
            }
            if (jmxFile != null) {
                jmxFile.deleteOnExit();
            }
        }
}

SummariserRunningSample total = summer.getTotal();
对于该段代码,使用jmeter的api是无法获取的,所以我们需要利用jvm的加载机制做一些操作。
我们在项目中定义一个名为org.apache.jmeter.reporters的包。
然后拷贝jmeter的Summariser类和SummariserRunningSample类到自定义的包中。在Summariser中新增方法:

public SummariserRunningSample getTotal(){
   return myTotals.total;
}

SummariserRunningSample类改为public修饰,
Summariser类中的内部静态类Totals改为public修饰,把内部静态类Totals中的total常量改为public修饰。
最后你就能愉快在Java中获取Jmeter的结果集了。

其他

关于Jmeter除了我写的这种方式在Java中做压测外,还有一种方式是通过Jmeter的HashTree来组装数据,HashTree其实就是Java代码版本的jmx文件。不过组装比较麻烦,我这两天还在研究如果通过HashTree来组装,等研究出来后,我再做分享。


# Jmeter