jmeter之GUI運行原理

1、一語道破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,使得咱們若是咱們真的要徹底進行配置文件的操做的話會頗有難度,不過我猜也沒有人會這麼作,由於這也太無聊了。html

知道了這些,那麼其實咱們就能夠知道咱們在jmeter作的全部操做,它的代碼是如何運行的了。結合API會使咱們閱讀源碼更容易。
 
2、解析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取樣器。
 
3、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的關係是等於。
 
其餘的部分與此相同,就不一一進行解讀了。 
 
4、源碼解讀
 
/**這個包名,也是這個文件所在的路徑,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

 至此,比較基本的東西都已說完,歡迎補充,共同窗習。java

更深層次的東西還需進一步閱讀源代碼,瞭解原理。apache

相關文章
相關標籤/搜索