jmeter之GUI运行原理

一、一语道破jmeter

大家都知道我们在应用jmeter的图形化界面来进行操作,保存后生成的是一个.jmx文件。

那么这个.jmx文件中都是些什么呢。

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.7" jmeter="2.12 r1636949">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
  </hashTree>
  </hashTree>
</jmeterTestPlan>

这是我截取的一段.jmx文件中的内容。

从文件中的第一行内容,<?xml version="1.0" encoding="UTF-8"?>

我们就可以很容易的看出来,这就是一个xml文件。一般在java开发中,我们使用这个文件来做配置文件。

综上,其实我们在jmeter的图形化界面的所有操作,其实就是在进行配置文件的配置,当然如果你对这个配置文件的书写规范足够熟悉的话就可以抛弃jmeter的GUI了。哈哈,不过这个得需要相当熟悉它的书写规范了。不过,不知道为什么,这个xml没有明确标注出来遵循哪个

XML Schema,使得我们如果我们真的要完全进行配置文件的操作的话会很有难度,不过我猜也没有人会这么做,因为这也太无聊了。

知道了这些,那么其实我们就可以知道我们在jmeter做的所有操作,它的代码是如何运行的了。结合API会使我们阅读源码更容易。

二、解析jmx文件

这是我截取的一段我们添加的线程组的.jmx文件中的内容,是一个java请求的jmx配置信息

   <hashTree>
        <JavaSampler guiclass="JavaTestSamplerGui" testclass="JavaSampler" testname="Java请求" enabled="true">
          <elementProp name="arguments" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" enabled="true">
            <collectionProp name="Arguments.arguments">
              <elementProp name="Sleep_Time" elementType="Argument">
                <stringProp name="Argument.name">Sleep_Time</stringProp>
                <stringProp name="Argument.value">100</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="Sleep_Mask" elementType="Argument">
                <stringProp name="Argument.name">Sleep_Mask</stringProp>
                <stringProp name="Argument.value">0xFF</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="Label" elementType="Argument">
                <stringProp name="Argument.name">Label</stringProp>
                <stringProp name="Argument.value"></stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="ResponseCode" elementType="Argument">
                <stringProp name="Argument.name">ResponseCode</stringProp>
                <stringProp name="Argument.value"></stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="ResponseMessage" elementType="Argument">
                <stringProp name="Argument.name">ResponseMessage</stringProp>
                <stringProp name="Argument.value"></stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="Status" elementType="Argument">
                <stringProp name="Argument.name">Status</stringProp>
                <stringProp name="Argument.value">OK</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="SamplerData" elementType="Argument">
                <stringProp name="Argument.name">SamplerData</stringProp>
                <stringProp name="Argument.value"></stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="ResultData" elementType="Argument">
                <stringProp name="Argument.name">ResultData</stringProp>
                <stringProp name="Argument.value"></stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="classname">org.apache.jmeter.protocol.java.test.JavaTest</stringProp>
        </JavaSampler>
        <hashTree/>

可以看出,一个java请求的整体是由一个hashTree标签进行标记的,事实上我们基本上所有的元素都是由hashTree进行标记的,个别的除外,如添加一个查看结果树,这个是由ResultCollector标签进行标记。 其中JavaSampler标签就是标记这是一个java请求,直译为java取样器。

三、GUI与jmx比对说明

这个添加的java请求是jmeter源码中自带的一个例子,GUI添加后如图:

从这里再结合jmx文件中,我们不难看出变量的名称和jmx中标签的对应关系,如:

Sleep_Time这个变量对应的jmx文件中的内容是:

              <elementProp name="Sleep_Time" elementType="Argument">
                <stringProp name="Argument.name">Sleep_Time</stringProp>
                <stringProp name="Argument.value">100</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>

其中,stringProp标签中,属性 name="Argument.name" 的值就是Sleep_Time

stringProp标签中,属性 name="Argument.value" 的值就是变量Sleep_Time的值

<stringProp name="Argument.metadata">=</stringProp> 这个标签说的是变量Sleep_Time和100的关系是等于。

其他的部分与此相同,就不一一进行解读了。

四、源码解读

/**这个包名,也是这个文件所在的路径,java自带的一个演示文件,
	英文好的同学可自行阅读,英文不好的可以和我一起阅读,
	互相学习,共同进步,如有错误欢迎指正。
	*/
package org.apache.jmeter.protocol.java.test;

import java.io.Serializable;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;

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;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

public class JavaTest extends AbstractJavaSamplerClient implements Serializable {

    private static final Logger LOG = LoggingManager.getLoggerForClass();

    private static final long serialVersionUID = 240L;

	/**这个变量是用来记录JavaSamplerContext对象context调用getLongParameter方法传递DEFAULT_SLEEP_TIME和SLEEP_NAME运算后的返回值*/
    private long sleepTime;

    /** 下边这个变量的值100就是Sleep_Time的默认值,也是我们在页面中看到的那个100,注意这个值是一个毫秒值. */
    public static final long DEFAULT_SLEEP_TIME = 100;

    /** 这个变量就是用来存储GUI中的那个Sleep_Time的变量。 */
    private static final String SLEEP_NAME = "Sleep_Time";

    /**
	以下的变量名称和GUI中的变量名称都是相同的,不难看懂。
     */
    private long sleepMask;

    public static final long DEFAULT_SLEEP_MASK = 0xff;

    private static final String DEFAULT_MASK_STRING = "0x" + (Long.toHexString(DEFAULT_SLEEP_MASK)).toUpperCase(java.util.Locale.ENGLISH);

    private static final String MASK_NAME = "Sleep_Mask";

    private String label;

    private static final String LABEL_NAME = "Label";

    private String responseMessage;

    private static final String RESPONSE_MESSAGE_DEFAULT = "";

    private static final String RESPONSE_MESSAGE_NAME = "ResponseMessage";

    private String responseCode;

    private static final String RESPONSE_CODE_DEFAULT = "";

    private static final String RESPONSE_CODE_NAME = "ResponseCode";

    private String samplerData;

    private static final String SAMPLER_DATA_DEFAULT = "";

    private static final String SAMPLER_DATA_NAME = "SamplerData";

    private String resultData;

    private static final String RESULT_DATA_DEFAULT = "";

    private static final String RESULT_DATA_NAME = "ResultData";

    private boolean success;

    private static final String SUCCESS_DEFAULT = "OK";

    private static final String SUCCESS_NAME = "Status";

    /**
     * 默认的构造函数,实例化了一个客户端的类
     */
    public JavaTest() {
        LOG.debug(whoAmI() + "\tConstruct");
    }

    /*
     * 这个方法就是设置了所有的值,我们从GUI中输入的值就是由这个方法进行读取并且赋值给上边定义的变量。
     */
    private void setupValues(JavaSamplerContext context) {

        sleepTime = context.getLongParameter(SLEEP_NAME, DEFAULT_SLEEP_TIME);
        sleepMask = context.getLongParameter(MASK_NAME, DEFAULT_SLEEP_MASK);

        responseMessage = context.getParameter(RESPONSE_MESSAGE_NAME, RESPONSE_MESSAGE_DEFAULT);

        responseCode = context.getParameter(RESPONSE_CODE_NAME, RESPONSE_CODE_DEFAULT);

        success = context.getParameter(SUCCESS_NAME, SUCCESS_DEFAULT).equalsIgnoreCase("OK");

        label = context.getParameter(LABEL_NAME, "");
        if (label.length() == 0) {
            label = context.getParameter(TestElement.NAME);
        }

        samplerData = context.getParameter(SAMPLER_DATA_NAME, SAMPLER_DATA_DEFAULT);

        resultData = context.getParameter(RESULT_DATA_NAME, RESULT_DATA_DEFAULT);
    }

    /**
	这是一个初始化方法,可以理解为loadrunner中的init
     */
    @Override
    public void setupTest(JavaSamplerContext context) {
        if (LOG.isDebugEnabled()) {
            LOG.debug(whoAmI() + "\tsetupTest()");
            listParameters(context);
        }
    }

    /**
		这个方法是一个与生命周期相关的方法
		决定了哪些变量参数是否显示到GUI页面上。这个方法类一加载就会运行,运行的顺序排在所有复写的方法中第一。
     */
    @Override
    public Arguments getDefaultParameters() {
        Arguments params = new Arguments();
        params.addArgument(SLEEP_NAME, String.valueOf(DEFAULT_SLEEP_TIME));
        params.addArgument(MASK_NAME, DEFAULT_MASK_STRING);
        params.addArgument(LABEL_NAME, "");
        params.addArgument(RESPONSE_CODE_NAME, RESPONSE_CODE_DEFAULT);
        params.addArgument(RESPONSE_MESSAGE_NAME, RESPONSE_MESSAGE_DEFAULT);
        params.addArgument(SUCCESS_NAME, SUCCESS_DEFAULT);
        params.addArgument(SAMPLER_DATA_NAME, SAMPLER_DATA_DEFAULT);
        params.addArgument(RESULT_DATA_NAME, SAMPLER_DATA_DEFAULT);
        return params;
    }

    /**
		主要运行的代码,相当于Loadrunner中的action
     */
    @Override
    public SampleResult runTest(JavaSamplerContext context) {
        setupValues(context);

        SampleResult results = new SampleResult();

        results.setResponseCode(responseCode);
        results.setResponseMessage(responseMessage);
        results.setSampleLabel(label);

        if (samplerData != null && samplerData.length() > 0) {
            results.setSamplerData(samplerData);
        }

        if (resultData != null && resultData.length() > 0) {
            results.setResponseData(resultData, null);
            results.setDataType(SampleResult.TEXT);
        }

        //这个方法很重要,是标记一个事务的开始,可以理解为loadrunner中的lr_start_transaction
        results.sampleStart();

        long sleep = sleepTime;
        if (sleepTime > 0 && sleepMask > 0) {
            long start = System.currentTimeMillis();

            sleep = sleepTime + (start % sleepMask);
        }

        try {

            if (sleep > 0) {
                TimeUnit.MILLISECONDS.sleep(sleep);
            }
            results.setSuccessful(success);
        } catch (InterruptedException e) {
            LOG.warn("JavaTest: interrupted.");
            results.setSuccessful(true);
        } catch (Exception e) {
            LOG.error("JavaTest: error during sample", e);
            results.setSuccessful(false);
        } finally {
            // 这里就是标记事务的结束,相当于loadrunner中的lr_end_transaction
            results.sampleEnd();
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(whoAmI() + "\trunTest()" + "\tTime:\t" + results.getTime());
            listParameters(context);
        }

        return results;
    }

    /**
		包含初始化参数的一个调试方法。
     */
    private void listParameters(JavaSamplerContext context) {
        Iterator<String> argsIt = context.getParameterNamesIterator();
        while (argsIt.hasNext()) {
            String name = argsIt.next();
            LOG.debug(name + "=" + context.getParameter(name));
        }
    }

    /**
		这个就是一个用户自定义的调试方法。
     */
    private String whoAmI() {
        StringBuilder sb = new StringBuilder();
        sb.append(Thread.currentThread().toString());
        sb.append("@");
        sb.append(Integer.toHexString(hashCode()));
        return sb.toString();
    }

}

//还有一个常见方法,这里没有写出来,那就是 	teardownTest,这个其实就可以理解为loadrunner中的end

至此,比较基本的东西都已说完,欢迎补充,共同学习。

更深层次的东西还需进一步阅读源代码,了解原理。

时间: 2024-10-31 08:37:50

jmeter之GUI运行原理的相关文章

解决Jmeter Non GUI运行时报“...ensure the jmeter .save.saveservice.* properties are the same as when the CSV file was created or the file may be read incorrectly”错误的问题

错误信息: File 'xxx.jtl' does not contain the field names header, ensure the jmeter.save.saveservice.* properties are the same as when the CSV file was created orthe file may be read incorrectly 解决: jmeter.properties配置文件 jmeter.save.saveservice.output_fo

Jmeter运行原理

Jmeter运行原理: JMETER是运行在JVM虚拟机上的,每个进程的开销比loadrunner的进程开销大,如果以进程的方式来运行每台负载机上的进程数量不会允许太多,当有大量并发时就需要大量的负载机,jmeter选择了以线程方式来运行.jmeter通过线程组来驱动多个线程运行测试脚本对被测服务器发起负载,每一个负载机上都可以运行多个线程组.jemter运行场景不仅可以再gui方式中完成,还可以运行命令行,而且命令行的运行方式对于负载机的资源消耗会更小.Jmeter基本原理是建立一个线程池,多

JMeter非GUI方式运行时动态设置线程组及传参

在使用JMeter进行性能测试自动化时,可能会有如下需求: 1.指定运行多少线程,指定运行多少次: 2.访问的目标地址变化了,端口也变化了,需要重新指定. 上面的需求如果有GUI方式运行,这都不是问题,直接在脚本上进行修改即可以了. 但是性能测试自动化是以非GUI方式运行的,如果要修改测试计划就比较麻烦了. 下面来说说如何简单的搞定这些问题: 1.指定运行多少线程   我们知道JMeter测试计划在运行Sampler之前先加载运行属性(jmeter.properties,system.prope

Jmeter结构体系及运行原理

Jmeter结构体系 把Jmeter的结构体系拆分为三维空间,如图: X1~X5:是负载模拟的一个过程,使用这些组件来完成负载的模拟: X1:选择协议,模拟用户请求,检查服务器响应是否正确,然后收集结果信息: X2:完善测试脚本部分,包括参数化,关联等: X3:控制测试脚本业务逻辑: X4:集合点,模拟用户并发: X5:用户数,一个线程代表一个用户: Y1:可以理解为选择协议,包含负载模拟部分,负责模拟用户请求: Y2:可以理解为检查点,结果验证部分,负责验证结果正确性: Z:可以理解为监控器,

JMeter非GUI界面运行

JMeter是一款可以用于做接口可以用于作压力性能的应用程序,该程序是纯Java语音开发,所有对环境支持都比较好. JMeter可以运行模式有两种,一种是UI图形,另一种是命令模式运行也就是非GUI模式.两种模式的区别还是挺大的. GUIi:由于是图形界面,所以在运行时会消耗很多资源,而且图形界面运行时结果是保存在Jmeter运行的内存中,如果是做长时的性能测试,内存就会占用的很高,首先就是影响性能结果的准确性,其次当内存增长到一定程度,就会报错,甚至可能出现卡死.1非GUIi:命令模式运行可以

计算机运行原理与操作系统基础

一.冯诺依曼体系 使用二进制 数据和代码存储在一起 存储器是按地址访问的线性编制的一维结构. 二.计算机五大部件 运算器(CA).控制器(CC).存储器(M,内存+外存).输入设备(I).输出设备(O) 三.计算机运行原理 五大部件中,以控制器为中心的指令流和以运算器为中心的数据量贯通其中,从而得出结果. 四.操作系统基础 目前大家所知道的操作系统有windows,mac,linux,unix等. 操作系统是运行在系统硬件上用于整合硬件资源的系统软件. 主要包括存储管理.进程管理.文件管理.设备

Jmeter命令行运行配置环境变量

Jmeter命令行运行配置环境变量 在打开jmeter GUI界面时会弹出cmd命令窗口提示:压测时不要用GUI,要用命令行 在cmd命令行里面运行jmeter的话,需要配置jmeter环境变量,下面开始配置jmeter环境变量: 下载完成后 如图我保存到    D:\apache-jmetes\apache-jmeter-4.0  开始配置环境变量: (1)“我的电脑”>鼠标右键菜单>"属性">"高级系统设置">"环境变量&quo

虚拟内存运行原理!

                                   虚拟内存运行原理! 在系统中运行的每个进程都需要使用到内存,但不是每个进程都需要每时每刻使用系统分配的内存空间.当系统运行所需内存超过实际的物理内存,内核会释放某些进程所占用但未使用的部分或所有物理内存,将这部分资料存储在磁盘上直到进程下一次调用,并将释放出的内存提供给有需要的进程使用. 在Linux内存管理中,主要是通过"调页Paging"和"交换Swapping"来完成上述的内存调度.调页算法

黑马公开课——运行原理与GC学习笔记

.NET Framework 程序的运行原理 .NET Framework的组成:(1)基础类库(BCL):使用线程的类来完成编程,对于不存在的类,就自己编写:(2)编译工具:将源文件,编译成"程序集"(exe或dll等)[.NET环境中,MSIL=CIL=IL](3)公共语言运行时(CLR):执行前检测.编译:执行到了某个方法时才编译这个方法的代码[即时编译器(JIT)]编译过程:.NET源代码(C#)-->通过C#编译器编译成程序集[程序集中包括:元数据(一个表,显示了程序中