1. 程式人生 > >jmeter寫外掛

jmeter寫外掛

JMeter Gui – TestElement約定

在編寫任何JMeter元件時,必須注意某些特定的約定——如果JMeter環境中正確地執行JMeter元件,那麼它將會執行。本部分描述了元件的GUI部分必須滿足的約定。
JMeter中的GUI程式碼嚴格地與測試元件程式碼(這裡指邏輯控制程式碼,下同)分離。因此,當編寫一個元件時,將會有一個用於測試元件的類,另一個用於GUI表示。GUI類是無狀態的,因此它不應該掛在對測試元件的引用上(儘管有例外)。

GUI元素應該繼承適當的抽象類:

  • AbstractSamplerGui
  • AbstractAssertionGui
  • AbstractConfigGui
  • AbstractControllerGui
  • AbstractPostProcessorGui
  • AbstractPreProcessorGui
  • AbstractVisualizer
  • AbstractTimerGui
  • ……

這些抽象類提供了大量的管道工作,不用擴充套件,用來代替直接實現介面。

已經繼承了適當的GUI類,剩下要遵循以下步驟:
1、實現getResourceLabel()
該方法返回資源的標題/名稱。
2、建立GUI。無論使用什麼樣式,都要佈局GUI。類最終要繼承JPanel,因此佈局必須在的類自己的ContentPane中。不要通過動作和事件將·GUI元素連線到測試元件類。讓swing的內部模型儘可能多地掛在所有資料上。
(1)一些標準的GUI內容應該新增到所有JMeter GUI元件中:
a、呼叫setBorder(makeBorder())。這將給它提供標準的JMeter邊框。
b、通過makeTitlePanel()新增標題窗格。通常這是新增到GUI中的第一件事,應該在一個垂直佈局方案中完成,或者使用JMeter的VerticalLayout類。下面是一個示例init()方法:

private void init() {
    setLayout(new BorderLayout());
    setBorder(makeBorder());
    Box box = Box.createVerticalBox();
    box.add(makeTitlePanel());
    box.add(makeSourcePanel());
    add(box,BorderLayout.NORTH);
    add(makeParameterPanel(),BorderLayout.CENTER);
}

3、實現public void configure(TestElement el)
(1)一定呼叫super.configure(e),這將填充一些資料,比如元素的名稱
(2)使用此方法將資料設定為GUI元素。例子

public void configure(TestElement el) {
    super.configure(el);
    useHeaders.setSelected(el.getPropertyAsBoolean(RegexExtractor.USEHEADERS));
    useBody.setSelected(!el.getPropertyAsBoolean(RegexExtractor.USEHEADERS));
    regexField.setText(el.getPropertyAsString(RegexExtractor.REGEX));
    templateField.setText(el.getPropertyAsString(RegexExtractor.TEMPLATE));
    defaultField.setText(el.getPropertyAsString(RegexExtractor.DEFAULT));
    matchNumberField.setText(el.getPropertyAsString(RegexExtractor.MATCH_NUM));
    refNameField.setText(el.getPropertyAsString(RegexExtractor.REFNAME));
}

(3)實現public void modifyTestElement(TestElement e),這是將資料從GUI元素移動到TestElement的地方。這是前一種方法的邏輯逆操作
a、呼叫super.configureTestElement(e),處理一些預設資料
b、例子

public void modifyTestElement(TestElement e) {
    super.configureTestElement(e);
    e.setProperty(new BooleanProperty(
            RegexExtractor.USEHEADERS,
            useHeaders.isSelected()));
    e.setProperty(RegexExtractor.MATCH_NUMBER,
            matchNumberField.getText());
    if (e instanceof RegexExtractor) {
        RegexExtractor regex = (RegexExtractor)e;
        regex.setRefName(refNameField.getText());
        regex.setRegex(regexField.getText());
        regex.setTemplate(templateField.getText());
        regex.setDefaultValue(defaultField.getText());
    }
}

(4)實現public TestElement createTestElement(),該方法應該建立TestElement類的一個新例項,然後將其傳遞modifyTestElement(TestElement)方法

public TestElement createTestElement() {
    RegexExtractor extractor = new RegexExtractor();
    modifyTestElement(extractor);
    return extractor;
}

不能保留對測試元件的引用的原因是因為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外掛分類

GUI的元件主要包括10大元件

  • ThreadGroup(執行緒組)
  • Test Fragment(測試片段)
  • logic Controller(邏輯控制器)
  • Config element(配置元件)
  • Timer(定時器)
  • pre processor(前置處理器)
  • post processor(後置處理器)
  • Sampler(測試抽樣器)
  • Assertion(斷言)
  • Listener(監聽器);

非GUI元件

  • Function(函式)

JMeter外掛式元件實現

TestElement是所有元件的最基本單元,元件類都是TestElement類的子類
依據上面介紹,元件的實現分兩部分:GUI和TestElement

GUI部分的實現

繼承實現對應的抽象類

抽象類 繼承的類 對應元件備註
AbstractAssertionGui AbstractScopedJMeterGuiComponent 斷言
AbstractConfigGui AbstractJMeterGuiComponent 配置
AbstractControllerGui AbstractJMeterGuiComponent 控制
AbstractPostProcessorGui AbstractScopedJMeterGuiComponent 後置處理器
AbstractPreProcessorGui AbstractJMeterGuiComponent 前置處理器
AbstractSamplerGui AbstractJMeterGuiComponent 取樣器
AbstractThreadGroupGui AbstractJMeterGuiComponent 執行緒組
AbstractTimerGui AbstractJMeterGuiComponent 定時器
AbstractListenerGui AbstractJMeterGuiComponent 監聽器
AbstractVisualizer AbstractListenerGui 元件依賴
AbstractScopedJMeterGuiComponent AbstractJMeterGuiComponent 元件依賴
AbstractJMeterGuiComponent JPanel 元件依賴
AbstractFunction Function 元件依賴
AbstractJMeterGuiComponent JPanel 元件依賴

邏輯控制實現

Assertion(斷言)元件

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

比如:

public class XMLSchemaAssertion extends AbstractTestElement implements Serializable, Assertion {
    ……
    @Override
    public AssertionResult getResult(SampleResult response) {
        AssertionResult result = new AssertionResult(getName());
        ……
        return result;
    }
    ……
}

再比如:

public abstract class AbstractScopedTestElement extends AbstractTestElement {...}
public abstract class AbstractScopedAssertion extends AbstractScopedTestElement{...}

public class DurationAssertion extends AbstractScopedAssertion implements Serializable, Assertion {
    public static final String DURATION_KEY = "DurationAssertion.duration"; 
    @Override
    public AssertionResult getResult(SampleResult response) {
        ……
        return result;
    }
    private long getAllowedDuration() {
        return getPropertyAsLong(DURATION_KEY);
    }
    public void setAllowedDuration(long duration) {
        setProperty(DURATION_KEY, duration);
    }
}

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 class ThreadGroupTest extends AbstractThreadGroup {
    private static final long serialVersionUID = 1L;
    @Override
    public void threadFinished(JMeterThread arg0) {}
    @Override
    public int numberOfActiveThreads() {return 0;}
    @Override
    public void start(int arg0, ListenerNotifier arg1, ListedHashTree arg2, StandardJMeterEngine arg3) {}
    @Override
    public void stop() {}
    @Override
    public boolean stopThread(String arg0, boolean arg1) {return false;}
    @Override
    public void tellThreadsToStop() {}
    @Override
    public boolean verifyThreadsStopped() {return false;}
    @Override
    public void waitThreadsStopped() {}
}

Timer(定時器)元件

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

public class TimerTest extends AbstractTestElement implements Timer{
    private static final long serialVersionUID = 1L;
    @Override
    public long delay() {
        return 0;
    }
}

控制執行緒延時,即用來模仿思考時間(ThinkTime)或鍵盤時間(KeyTime);

ctronlThread

控制執行緒行為,如SyncTimer(同步計時器),就是內部利用CyclicBarrier來控制阻塞和釋放全部執行執行緒的邏輯行為,從而達到“集合點”的目的。

public class SyncTimer extends AbstractTestElement implements Timer, Serializable, TestBean, TestStateListener, ThreadListener {
    private static final Logger log = LoggerFactory.getLogger(SyncTimer.class);
    private static class BarrierWrapper implements Cloneable {
        private CyclicBarrier barrier;
        public BarrierWrapper() {
            this.barrier = null;
        }
        public BarrierWrapper(int parties) {
            this.barrier = new CyclicBarrier(parties);
        }
        public synchronized void setup(int parties) {
            if(this.barrier== null) {
                this.barrier = new CyclicBarrier(parties);
            }
        }
        public int await() throws InterruptedException, BrokenBarrierException{
            return barrier.await();
        }
        public int await(long timeout, TimeUnit timeUnit) throws InterruptedException, BrokenBarrierException, TimeoutException {
            return barrier.await(timeout, timeUnit);
        }
        public void reset() {
            barrier.reset();
        }
        @Override
        protected Object clone()  {
            BarrierWrapper barrierWrapper=  null;
            try {
                barrierWrapper = (BarrierWrapper) super.clone();
                barrierWrapper.barrier = this.barrier;
            } catch (CloneNotSupportedException e) {
            }
            return barrierWrapper;
        }
    }
    ……
    public void threadStarted() {
        if(getGroupSize() == 0) {
            int numThreadsInGroup = JMeterContextService.getContext().getThreadGroup().getNumThreads();
            // Unique Barrier creation ensured by synchronized setup
            this.barrier.setup(numThreadsInGroup);
        }
    }
    ……
}


這裡寫圖片描述

pre processor(前置處理器)元件

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

比如:BeanShellPreProcessor

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();
    }
}

作用:對執行緒上下文中的當前Sampler和前一個SampleResult進行識別和判斷。

post processor(後置處理器)元件

post processor(後置處理器)元件通過繼承AbstractTestElement抽象類,實現PostProcessor介面的process ()方法控制邏輯

public abstract class AbstractScopedTestElement extends AbstractTestElement {……}

public class RegexExtractor extends AbstractScopedTestElement implements PostProcessor, Serializable {
    ……
  @Override
  public void process() {}
  ……
}

作用:對執行緒上下文中的前一個SampleResult進行識別和判斷。

Controller(控制器)元件

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

public class ForeachController extends GenericController implements Serializable {
    public ForeachController() {}
    ……
    @Override
    public boolean isDone() {}
    @Override
    public Sampler next() {}    
    @Override
    protected Sampler nextIsNull() throws NextIsNullException {}
    @Override
    protected int getIterCount() {return loopCount+1;}
    @Override
    protected void reInitialize() {}
    @Override
    public void triggerEndOfLoop() {}
    @Override
    public void initialize() {}
}

Sampler(測試抽樣器)元件

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

public class DebugSampler extends AbstractSampler implements TestBean {
    ……
    @Override
    public SampleResult sample(Entry e) {}
    ……
}

Listener(監聽器)

直接繼承AbstractTestElement,實現sampleListener或Visualizer等介面方法

public class Summariser extends AbstractTestElement
    implements Serializable, SampleListener, TestStateListener, NoThreadClone, Remoteable {
    ……
    @Override
    @SuppressWarnings("SynchronizeOnNonFinalField")
    public void sampleOccurred(SampleEvent e) {
        SampleResult s = e.getResult();
        if(IGNORE_TC_GENERATED_SAMPLERESULT && TransactionController.isFromTransactionController(s)) {
            return;
        }
        long now = System.currentTimeMillis() / 1000;// in seconds
        SummariserRunningSample myDelta = null;
        SummariserRunningSample myTotal = null;
        boolean reportNow = false;
        synchronized (myTotals) {
            if (s != null) {
                myTotals.delta.addSample(s);
            }
            if ((now > myTotals.last + INTERVAL_WINDOW) && (now % INTERVAL <= INTERVAL_WINDOW)) {
                reportNow = true;
                // copy the data to minimise the synch time
                myDelta = new SummariserRunningSample(myTotals.delta);
                myTotals.moveDelta();
                myTotal = new SummariserRunningSample(myTotals.total);
                myTotals.last = now; // stop double-reporting
            }
        }
        if (reportNow) {
            formatAndWriteToLog(myName, myDelta, "+");
            // Only if we have updated them
            if (myTotal != null && myDelta != null &&myTotal.getNumSamples() != myDelta.getNumSamples()) { // NOSONAR
                formatAndWriteToLog(myName, myTotal, "=");
            }
        }
    }
    ……
    }

可以從實際用途上將其分為兩大類Report (報告)和Visualizers(監視器)。
Report (報告)繼承AbstractListenerElement抽象類,通過實現sampleOccurred(SampleEvent e)方法,對所有采集事件中所產生的SampleResult進行處理,從而生成報告;

public class ResultCollector extends AbstractListenerElement implements SampleListener, Clearable, Serializable,
        TestStateListener, Remoteable, NoThreadClone {
    ……
    @Override
    public void sampleOccurred(SampleEvent event) {
        SampleResult result = event.getResult();

        if (isSampleWanted(result.isSuccessful())) {
            sendToVisualizer(result);
            if (out != null && !isResultMarked(result) && !this.isStats) {
                SampleSaveConfiguration config = getSaveConfig();
                result.setSaveConfig(config);
                try {
                    if (config.saveAsXml()) {
                        SaveService.saveSampleResult(event, out);
                    } else { // !saveAsXml
                        String savee = CSVSaveService.resultToDelimitedString(event);
                        out.println(savee);
                    }
                } catch (Exception err) {
                    log.error("Error trying to record a sample", err); // should throw exception back to caller
                }
            }
        }
        if(summariser != null) {
            summariser.sampleOccurred(event);
        }
    }
    ……
}

Visualizers(監視器)主要用於特定的監控任務,比如監控系統資源利用率的元件,與Report的區別在於Visualizers必須繼承一個 ResultCollector類,並在收集器中通過開啟額外執行緒方式完成自定義的資料採集。

public class ResultCollector extends AbstractListenerElement implements SampleListener, Clearable, Serializable, TestStateListener, Remoteable, NoThreadClone {……}

比如標準外掛中自定義一個JMXMonCollector

public class CorrectedResultCollector extends ResultCollector {}

public class JMXMonCollector extends CorrectedResultCollector implements Runnable, JMXMonSampleGenerator {……}

注意:對於一個需要配置的元件類則需要實現ConfigMergabilityIndicator介面的public boolean applies(ConfigTestElement configElement)方法,用來指明哪些Config元件可以用來對其進行配置

這裡參考DebugSampler的原始碼如下:

public class DebugSampler extends AbstractSampler implements TestBean {
    ……
    private static final Set<String> APPLIABLE_CONFIG_CLASSES = new HashSet<>(
            Arrays.asList("org.apache.jmeter.config.gui.SimpleConfigGui"));
    @Override
    public boolean applies(ConfigTestElement configElement) {
        String guiClass = configElement.getProperty(TestElement.GUI_CLASS).getStringValue();
        return APPLIABLE_CONFIG_CLASSES.contains(guiClass);
    }
    ……
}

以上程式碼指明SimpleConfigGui配置元件可以對DebugSampler元件進行配置。

函式

Function(函式)是非GUI元件,這類元件的實現比較簡單,而且功能比較單一,只需要繼承相應的抽象類即可~

寫一個求字元長度的函式:StrLen

public class StrLen extends AbstractFunction {
    private static final List<String> desc = new LinkedList<String>();
    private static final String KEY = "__strLen";
    static {
        desc.add("String to measure length");
        desc.add("Name of variable in which to store the result (optional)");
    }
    private Object[] values;
    public StrLen() {
    }
    @Override
    public synchronized String execute(SampleResult previousResult, Sampler currentSampler)
            throws InvalidVariableException {
        JMeterVariables vars = getVariables();
        Integer len = ((CompoundVariable) values[0]).execute().length();
        if (vars != null && values.length > 1) {
            String varName = ((CompoundVariable) values[1]).execute().trim();
            vars.put(varName, len.toString());
        }
        return len.toString();
    }
    @Override
    public synchronized void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException {
        checkMinParameterCount(parameters, 1);
        values = parameters.toArray();
    }
    @Override
    public String getReferenceKey() {
        return KEY;
    }
    public List<String> getArgumentDesc() {
        return desc;
    }
}

附錄

jmeter一些GUI類繼承關係

GUI類 繼承的類
ResultActionGui AbstractPostProcessorGui
ResultSaverGui AbstractListenerGui
SummariserGui AbstractListenerGui
TestBeanGUI AbstractJMeterGuiComponent
ThreadGroupGui AbstractThreadGroupGui
SetupThreadGroupGui ThreadGroupGui
PostThreadGroupGui ThreadGroupGui
WorkBenchGui AbstractJMeterGuiComponent
AssertionGui AbstractAssertionGui
BeanShellAssertionGui AbstractAssertionGui
urationAssertionGui AbstractAssertionGui
HTMLAssertionGui AbstractAssertionGui
MD5HexAssertionGUI AbstractAssertionGui
SizeAssertionGui AbstractAssertionGui
SMIMEAssertionGui AbstractAssertionGui
XMLAssertionGui AbstractAssertionGui
XMLSchemaAssertionGUI AbstractAssertionGui
XPathAssertionGui AbstractAssertionGui
CriticalSectionControllerGui AbstractControllerGui
IncludeControllerGui AbstractControllerGui
InterleaveControlGui AbstractControllerGui
ModuleControllerGui AbstractControllerGui
OnceOnlyControllerGui AbstractControllerGui
RandomControlGui AbstractControllerGui
RandomOrderControllerGui LogicControllerGui
SwitchControllerGui AbstractControllerGui
ThroughputControllerGui AbstractControllerGui
HtmlExtractorGui AbstractPostProcessorGui
RegexExtractorGui AbstractPostProcessorGui
XPathExtractorGui AbstractPostProcessorGui
JSONPostProcessorGui AbstractPostProcessorGui
CounterConfigGui AbstractConfigGui
SampleTimeoutGui AbstractPreProcessorGui
UserParametersGui AbstractPreProcessorGui
TestActionGui AbstractSamplerGui
AbstractRandomTimerGui AbstractTimerGui
ConstantTimerGui AbstractTimerGui
GaussianRandomTimerGui AbstractRandomTimerGui
PoissonRandomTimerGui AbstractRandomTimerGui
UniformRandomTimerGui AbstractRandomTimerGui
AbstractAssertionGui AbstractScopedJMeterGuiComponent
AbstractConfigGui AbstractJMeterGuiComponent
LoginConfigGui AbstractConfigGui
ObsoleteGui AbstractJMeterGuiComponent
SimpleConfigGui AbstractConfigGui
AbstractControllerGui AbstractJMeterGuiComponent
LogicControllerGui AbstractControllerGui
RunTimeGui AbstractControllerGui
TestFragmentControllerGui AbstractControllerGui
TestPlanGui AbstractJMeterGuiComponent
TransactionControllerGui AbstractControllerGui
WhileControllerGui AbstractControllerGui
WorkBenchGui AbstractJMeterGuiComponent
FunctionHelper JDialog
AbstractJMeterGuiComponent JPanel
AbstractScopedJMeterGuiComponent AbstractJMeterGuiComponent
AbstractPostProcessorGui AbstractScopedJMeterGuiComponent
AbstractPreProcessorGui AbstractJMeterGuiComponent
ResultActionGui AbstractPostProcessorGui
ResultSaverGui AbstractListenerGui
SummariserGui AbstractListenerGui
AbstractSamplerGui AbstractJMeterGuiComponent
TestBeanGUI AbstractJMeterGuiComponent
AbstractThreadGroupGui AbstractJMeterGuiComponent
PostThreadGroupGui ThreadGroupGui
SetupThreadGroupGui ThreadGroupGui
ThreadGroupGui AbstractThreadGroupGui
AbstractTimerGui AbstractJMeterGuiComponent
AbstractListenerGui AbstractJMeterGuiComponent
AbstractVisualizer AbstractListenerGui