Jmeter添加插件

JMeter插件分類

GUI組件 非GUI組件
ThreadGroup(線程組)
Test Fragment(測試片段)
logic Controller(邏輯控制器)
Config element(配置元件)
Timer(定時器)
pre processor(前置處理器)
post processor(后置處理器)
Sampler(測試抽樣器)
Assertion(斷言)
Listener(監聽器)
Function(函數)

JMeter Gui – TestElement約定

在編寫任何JMeter組件時,必須注意某些特定的約定——如果JMeter環境中正確地運行JMeter組件,那么它將會運行。本部分描述了組件的GUI部分必須滿足的約定。

JMeter中的GUI代碼嚴格地與測試元件代碼(這里指邏輯控制代碼,下同)分離。因此,當編寫一個組件時,將會有一個用于測試元件的類,另一個用于GUI表示。GUI類是無狀態的,因此它不應該掛在對測試元件的引用上(盡管有例外)。
GUI元素應該繼承適當的抽象類:

AbstractSamplerGui

AbstractAssertionGui

AbstractConfigGui

AbstractControllerGui

AbstractPostProcessorGui

AbstractPreProcessorGui

AbstractVisualizer

AbstractTimerGui

……

這些抽象類提供了大量的管道工作,不用擴展,用來代替直接實現接口。

已經繼承了適當的GUI類,剩下要遵循以下步驟:

  • 實現getResourceLabel()

該方法返回資源的標題/名稱。

  • 創建GUI
    無論使用什么樣式,都要布局GUI。類最終要繼承JPanel,因此布局必須在的類自己的ContentPane中。不要通過動作和事件將·GUI元素連接到測試元件類。讓swing的內部模型盡可能多地掛在所有數據上。

    • 一些標準的GUI內容應該添加到所有JMeter GUI組件中:

      • 調用setBorder(makeBorder())。這將給它提供標準的JMeter邊框。

      • 通過makeTitlePanel()添加標題窗格。通常這是添加到GUI中的第一件事,應該在一個垂直布局方案中完成,或者使用JMeter的VerticalLayout類。下面是一個示例init()方法:

  • 實現public void configure(TestElement el)

    • 一定調用super.configure(e),這將填充一些數據,比如元素的名稱

    • 使用此方法將數據設置為GUI元素。如下例

  • 實現public void modifyTestElement(TestElement e),這是將數據從GUI元素移動到TestElement的地方。這是前一種方法的邏輯逆操作

首先調用super.configureTestElement(e),處理一些默認數據;如下圖

  • 實現public TestElement createTestElement(),該方法應該創建TestElement類的一個新實例,然后將其傳遞modifyTestElement(TestElement)方法

不能保留對測試元件的引用的原因是因為JMeter的測試元件重用了多個GUI類對象的實例。這樣可以節省很多內存。它還使得編寫新組件的GUI部分變得非常容易。您仍然需要與Swing中的布局進行斗爭,但是不必擔心如何創建正確的事件和從GUI元素中獲取數據放入測試元件中。JMeter知道什么時候調用自定義配置,以及可以用一種非常簡單的方式來完成它的修改。

總結:

GUI與測試元件分離:GUI部分通過繼承各種組件GUI抽象類,測試元件部分通過繼承組件抽象類和實現各種接口方式從而實現不同組件的內部邏輯控制;

GUI與測試元件不分離:與分離方法的區別在于不單獨實現GUI部分,在測試元件部分通過實現TestBean接口方法從而實現對GUI界面的配置。(TestBean是一個空接口:Marker interface to tell JMeter to make a Test Bean Gui for the class)

JMeter插件組件實現

TestElement是所有組件的最基本單元,組件類都是TestElement類的子類
組件的實現需完成兩部分:GUITestElement

GUI部分的實現

繼承并實現對應的抽象類

1.png

邏輯控制實現

Assertion(斷言)組件

Assertion(斷言)組件通過繼承AbstractTestElement抽象類(或者AbstractTestElement子類),實現Assertion接口的getResult(SampleResult result)方法對結果內容進行判斷,從而實現斷言方法,用于對Sampler組件所產生的抽樣采集結果內容進行斷言。

XPathAssertion
摘自Jmeter源碼

public class XPathAssertion extends AbstractScopedAssertion implements Serializable, Assertion {
 
    @Override
    public AssertionResult getResult(SampleResult response) {
        // no error as default
        AssertionResult result = new AssertionResult(getName());
        result.setFailure(false);
        result.setFailureMessage("");

        byte[] responseData = null;
        Document doc = null;

        try {
            if (isScopeVariable()){
                String inputString=getThreadContext().getVariables().get(getVariableName());
                if (!StringUtils.isEmpty(inputString)) {
                    responseData = inputString.getBytes(StandardCharsets.UTF_8);
                } 
            } else {
                responseData = response.getResponseData();
            }
          .....
    }

Config(配置元件)組件

Config(配置元件)組件相對其他組件比較特殊,通過繼承ConfigTestElement類或只需要GUI部分的實現即可完成本體任務

public class CSVDataSet extends ConfigTestElement 
    implements TestBean, LoopIterationListener, NoConfigMerge {
    private static final Logger log = LoggerFactory.getLogger(CSVDataSet.class);
}

ThreadGroup(線程組)組件

ThreadGroup(線程組)組件繼承AbstractThreadGroup抽象類,通過重寫各類控制方法來達到控制和協調各線程(虛擬用戶)的行為,線程組是構建一個性能測試模型的最基本組件.

public abstract class AbstractThreadGroup extends AbstractTestElement 
    implements Serializable, Controller, JMeterThreadMonitor, TestCompilerHelper {
...
 /** {@inheritDoc} */
    @Override
    public boolean isDone() {
        return getSamplerController().isDone();
    }

    /** {@inheritDoc} */
    @Override
    public Sampler next() {
        return getSamplerController().next();
    }
    @Override
    public void addTestElement(TestElement child) {
        getSamplerController().addTestElement(child);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean addTestElementOnce(TestElement child){
        if (children.putIfAbsent(child, DUMMY) == null) {
            addTestElement(child);
            return true;
        }
        return false;
    }
...
}

Timer(定時器)組件

Timer(定時器)組件通過繼承AbstractTestElement抽象類,實現Timer接口的delay()方法來實現對時間的控制

public class ConstantTimer extends AbstractTestElement implements Timer, Serializable, LoopIterationListener {
   ..... 
   @Override
    public long delay() {
        return delay;
    }
}

控制線程延時,即用來模仿思考時間(ThinkTime)或鍵盤時間(KeyTime)
控制線程行為,如SyncTimer(同步計時器),就是內部利用CyclicBarrier來控制阻塞和釋放全部運行線程的邏輯行為,從而達到“集合點”的目的。

pre processor(前置處理器)組件

pre processor(前置處理器)組件通過繼承AbstractTestElement抽象類,實現PreProcessor接口的process ()方法控制邏輯

@GUIMenuSortOrder(Integer.MAX_VALUE)
public class BeanShellPreProcessor extends BeanShellTestElement
    implements Cloneable, PreProcessor, TestBean
{
    private static final Logger log = LoggerFactory.getLogger(BeanShellPreProcessor.class);

    private static final long serialVersionUID = 5;

    // can be specified in jmeter.properties
    private static final String INIT_FILE = "beanshell.preprocessor.init"; //$NON-NLS-1$

    @Override
    protected String getInitFileProperty() {
        return INIT_FILE;
    }

    @Override
    public void process(){
        final BeanShellInterpreter bshInterpreter = getBeanShellInterpreter();
        if (bshInterpreter == null) {
            log.error("BeanShell not found");
            return;
        }
        JMeterContext jmctx = JMeterContextService.getContext();
        Sampler sam = jmctx.getCurrentSampler();
        try {
            // Add variables for access to context and variables
            bshInterpreter.set("sampler", sam);//$NON-NLS-1$
            processFileOrScript(bshInterpreter);
        } catch (JMeterException e) {
            if (log.isWarnEnabled()) {
                log.warn("Problem in BeanShell script. {}", e.toString());
            }
        }
    }
    
    @Override
    public Object clone() {
        return super.clone();
    }
}

post processor(后置處理器)組件

post processor(后置處理器)組件通過繼承AbstractTestElement抽象類,實現PostProcessor接口的process ()方法控制邏輯

@GUIMenuSortOrder(Integer.MAX_VALUE)
public class BeanShellPostProcessor extends BeanShellTestElement
    implements Cloneable, PostProcessor, TestBean
{
    private static final Logger log = LoggerFactory.getLogger(BeanShellPostProcessor.class);

    private static final long serialVersionUID = 5;
    
    // can be specified in jmeter.properties
    private static final String INIT_FILE = "beanshell.postprocessor.init"; //$NON-NLS-1$

    @Override
    protected String getInitFileProperty() {
        return INIT_FILE;
    }

    @Override
    public void process() {
        JMeterContext jmctx = JMeterContextService.getContext();

        SampleResult prev = jmctx.getPreviousResult();
        if (prev == null) {
            return; // TODO - should we skip processing here?
        }
        final BeanShellInterpreter bshInterpreter = getBeanShellInterpreter();
        if (bshInterpreter == null) {
            log.error("BeanShell not found");
            return;
        }

        try {
            // Add variables for access to context and variables
            bshInterpreter.set("data", prev.getResponseData());//$NON-NLS-1$
            processFileOrScript(bshInterpreter);
        } catch (JMeterException e) {
            if (log.isWarnEnabled()) {
                log.warn("Problem in BeanShell script: {}", e.toString());
            }
        }
    }
     
    @Override
    public Object clone() {
        return super.clone();
    }
}

Controller(控制器)組件

Controller(控制器)組件通過繼承GenericController類
foreach,重寫isDone、next、nextIsNull、getIterCount、reInitialize、initialize、triggerEndOfLoop

@GUIMenuSortOrder(5)
public class ForeachController extends GenericController implements Serializable, IteratingController {
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isDone() {
        if (loopCount >= getEndIndex()) {
            return true;
        }
        JMeterContext context = getThreadContext();
        StringBuilder builder = new StringBuilder(
                getInputVal().length()+getSeparator().length()+3);
        String inputVariable = 
                builder.append(getInputVal())
                .append(getSeparator())
                .append(Integer.toString(loopCount+1)).toString();
        final JMeterVariables variables = context.getVariables();
        final Object currentVariable = variables.getObject(inputVariable);
        if (currentVariable != null) {
            variables.putObject(getReturnVal(), currentVariable);
            if (log.isDebugEnabled()) {
                log.debug("{} : Found in vars:{}, isDone:{}",
                        getName(), inputVariable, Boolean.FALSE);

            }
            return false;
        }
        return super.isDone();
    }

    // Prevent entry if nothing to do
    @Override
    public Sampler next() {
        try {
            if (breakLoop || emptyList()) {
                resetBreakLoop();
                reInitialize();
                resetLoopCount();
                return null;
            }
            return super.next();
        } finally {
            updateIterationIndex(getName(), loopCount);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Sampler nextIsNull() throws NextIsNullException {
        reInitialize();
        // Conditions to reset the loop count
        if (breakLoop
                || endOfArguments() // no more variables to iterate
                || loopCount >= getEndIndex() // we reached end index
                ) {
            resetBreakLoop();
            resetLoopCount();
            return null;
        }
        return next();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected int getIterCount() {
        return loopCount + 1;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void reInitialize() {
        setFirst(true);
        resetCurrent();
        incrementLoopCount();
        recoverRunningVersion();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void triggerEndOfLoop() {
        super.triggerEndOfLoop();
        resetLoopCount();
    }

    /**
     * Reset loopCount to Start index
     * @see org.apache.jmeter.control.GenericController#initialize()
     */
    @Override
    public void initialize() {
        super.initialize();
        loopCount = getStartIndex();
    }

    @Override
    public void startNextLoop() {
        reInitialize();
    }

    @Override
    public void breakLoop() {
        breakLoop = true;
        setFirst(true);
        resetCurrent();
        resetLoopCount();
        recoverRunningVersion();
    }

    @Override
    public void iterationStart(LoopIterationEvent iterEvent) {
        reInitialize();
        resetLoopCount();
    }
}

Sampler(測試抽樣器)組件

Sampler(測試抽樣器)組件繼承AbstractSampler抽象類,通過重寫SampleResult sample(Entry e)方法,實現測試過程以及測試結果的采集功能。

@GUIMenuSortOrder(2)
public class DebugSampler extends AbstractSampler implements TestBean {

....
    @Override
    public SampleResult sample(Entry e) {
        SampleResult res = new SampleResult();
        res.setSampleLabel(getName());
        res.sampleStart();
        StringBuilder sb = new StringBuilder(100);
        StringBuilder rd = new StringBuilder(20); // for request Data
        if (isDisplayJMeterVariables()){
            rd.append("JMeterVariables\n");
            sb.append("JMeterVariables:\n");
            formatSet(sb, JMeterContextService.getContext().getVariables().entrySet());
            sb.append("\n");
        }
    }
....
}

Listener(監聽器)

直接繼承AbstractTestElement,實現sampleListenerVisualizer等接口方法

@GUIMenuSortOrder(Integer.MAX_VALUE)
public class BeanShellListener extends BeanShellTestElement
    implements Cloneable, SampleListener, TestBean, Visualizer, UnsharedComponent  {
@Override
    protected String getInitFileProperty() {
        return INIT_FILE;
    }

    @Override
    public void sampleOccurred(SampleEvent se) {
        final BeanShellInterpreter bshInterpreter = getBeanShellInterpreter();
        if (bshInterpreter == null) {
            log.error("BeanShell not found");
            return;
        }

        SampleResult samp=se.getResult();
        try {
            bshInterpreter.set("sampleEvent", se);//$NON-NLS-1$
            bshInterpreter.set("sampleResult", samp);//$NON-NLS-1$
            processFileOrScript(bshInterpreter);
        } catch (JMeterException e) {
            if (log.isWarnEnabled()) {
                log.warn("Problem in BeanShell script. {}", e.toString());
            }
        }
    }

    @Override
    public void sampleStarted(SampleEvent e) {
        // NOOP
    }

    @Override
    public void sampleStopped(SampleEvent e) {
        // NOOP
    }

    @Override
    public void add(SampleResult sample) {
        // NOOP
    }

    @Override
    public boolean isStats() { // Needed by Visualizer interface
        return false;
    }

    @Override
    public Object clone() {
        return super.clone();
    }
}

可以從實際用途上將其分為兩大類Report (報告)和Visualizers(監視器)。

Report (報告)繼承AbstractListenerElement抽象類,通過實現sampleOccurred(SampleEvent e)方法,對所有采集事件中所產生的SampleResult進行處理,從而生成報告

Visualizers(監視器)主要用于特定的監控任務,比如監控系統資源利用率的組件,與Report的區別在于Visualizers必須繼承一個 ResultCollector類,并在收集器中通過開啟額外線程方式完成自定義的數據采集。

Function(函數)

請查閱 Jmeter擴展自定義函數

附錄

JMeter一些GUI類繼承關系



轉載于https://blog.csdn.net/yue530tomtom/article/details/77649872

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 公司最近需要測試后臺性能,所以學習使用了Jmeter,在此做記錄,也分享給更多需要的人。 這篇文章是 JMeter...
    顧顧314閱讀 4,298評論 0 10
  • 主要文體來自 CDNS:https://www.cnblogs.com/ceshisanren/p/5639895...
    Amano閱讀 11,059評論 3 27
  • 在使用Jmeter進行接口的性能測試時,由于Jmeter 是JAVA應用,對于CPU和內存的消耗比較大,所以,當需...
    燕京博士閱讀 4,204評論 0 16
  • 摘要## JMeter本身提供了插件機制,允許第三方擴展JMeter以支持JMeter不支持的協議的測試。本文以擴...
    zhulibin2012閱讀 1,522評論 1 2
  • 眼睛純凈,才能看見美麗的風景;心靈純凈,才能擁有純粹的感情。一個內心純凈的人,必定見過人世的復雜與陰暗,也經歷過世...
    屈道秋閱讀 273評論 0 0