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
类的子类
组件的实现需完成两部分:GUI
和TestElement
GUI部分的实现
继承并实现对应的抽象类
逻辑控制实现
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
,实现sampleListener
或Visualizer
等接口方法
@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