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);
控制執行緒行為,如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 |