• 【转】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() + " Construct");
    }
    /*
    * 这个方法就是设置了所有的值,我们从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() + " setupTest()");
    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() + " runTest()" + " Time: " + 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
      至此,比较基本的东西都已说完,欢迎补充,共同学习。
      更深层次的东西还需进一步阅读源代码,了解原理。
  • 相关阅读:
    继承的基本概念: (1)Java不支持多继承,也就是说子类至多只能有一个父类。 (2)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法。 (3)子类中定义的成员变量和父类中定义的成员变量相同时,则父类中的成员变量不能被继承。 (4)子类中定义的成员方法,并且这个方法的名字返回类型,以及参数个数和类型与父类的某个成员方法完全相同,则父类的成员方法不能被继承。 分析以上程
    为什么Java中Long类型的比float类型的范围小?
    html中<b>标签和<Strong>标签的区别
    在Eclipse中无法链接到svn,出现Previous operation has not finished; run 'cleanup' if it was interrupted异常
    java项目中登陆时记住密码
    部署java项目到服务器
    echarts-------饼形图
    ZTree的api....
    bootstrap loadStep流程节点动态显示
    解析properties文件
  • 原文地址:https://www.cnblogs.com/blongfree/p/4980853.html
Copyright © 2020-2023  润新知