性能测试工具 Jmeter 插件开发

woyebuzhidaowoshishei · October 16, 2016 · Last by kingTester replied at May 07, 2019 · 2351 hits

一. 配置源码

  1. 下载源码包 地址
  2. 导入源码包,我这里使用的工具 IntelliJ。直接 File->open 就行。导入后的项目目录如下图: 项目目录
  3. 下载 jar 包。jar 包下载下来的地址为:/lib
    下载jar
  4. 添加 jar 包。File->Project Structure->Libraries
  5. install install
  6. 运行 src->core->apache.jmeter->NewDriver

    public static void main(String[] args) {
    
        Thread.currentThread().setContextClassLoader(loader);
        if (System.getProperty("log4j.configuration") == null) {// $NON-NLS-1$ $NON-NLS-2$
            File conf = new File(jmDir, "bin" + File.separator + "log4j.conf");// $NON-NLS-1$ $NON-NLS-2$
            System.setProperty("log4j.configuration", "file:" + conf);
        }
    
        try {
            Class<?> initialClass = loader.loadClass("org.apache.jmeter.JMeter");// $NON-NLS-1$
            Object instance = initialClass.newInstance();
            Method startup = initialClass.getMethod("start", new Class[] { new String[0].getClass() });// $NON-NLS-1$
            startup.invoke(instance, new Object[] { args });
        } catch(Throwable e){
            e.printStackTrace();
            System.err.println("JMeter home directory was detected as: "+jmDir);
        }
    }
    

    从上述代码中我们可以看出,其实之际上调用的是 src->core->apache.jmeter->Jmeter 的 start() 方法。而当我们打开 Jmeter 的 Gui 时,实际上调用的用的是 src->core->apache.jmeter->Jmeter 的 startGui() 方法

private void startGui(String testFile) {
        String jMeterLaf = LookAndFeelCommand.getJMeterLaf();
        try {
            UIManager.setLookAndFeel(jMeterLaf);
        } catch (Exception ex) {
            log.warn("Could not set LAF to:"+jMeterLaf, ex);
        }

        PluginManager.install(this, true);

        JMeterTreeModel treeModel = new JMeterTreeModel();
        JMeterTreeListener treeLis = new JMeterTreeListener(treeModel);
        final ActionRouter instance = ActionRouter.getInstance();
        instance.populateCommandMap();  //这个方法会去寻找<your project>/lib/ext 下所有的jar
        treeLis.setActionHandler(instance);
        // NOTUSED: GuiPackage guiPack =
        GuiPackage.getInstance(treeLis, treeModel);
        MainFrame main = new MainFrame(treeModel, treeLis);
        ComponentUtil.centerComponentInWindow(main, 80);
        main.setVisible(true);
        instance.actionPerformed(new ActionEvent(main, 1, ActionNames.ADD_ALL));
        if (testFile != null) {
            try {
                File f = new File(testFile);
                log.info("Loading file: " + f);
                FileServer.getFileServer().setBaseForScript(f);

                HashTree tree = SaveService.loadTree(f);

                GuiPackage.getInstance().setTestPlanFile(f.getAbsolutePath());

                Load.insertLoadedTree(1, tree);
            } catch (ConversionException e) {
                log.error("Failure loading test file", e);
                JMeterUtils.reportErrorToUser(SaveService.CEtoString(e));
            } catch (Exception e) {
                log.error("Failure loading test file", e);
                JMeterUtils.reportErrorToUser(e.toString());
            }
        } else {
            JTree jTree = GuiPackage.getInstance().getMainFrame().getTree();
            TreePath path = jTree.getPathForRow(0);
            jTree.setSelectionPath(path);
            FocusRequester.requestFocus(jTree);
        }
    }

二.开发 sampler

JMeter 加载插件的机制比较简单,扫描扩展下的的所有实现了 JMeterGUIComponent 和 TestBean 接口的类,然后进行初始化。

ClassFinder.findClassesThatExtend(
    JMeterUtils.getSearchPaths(), 
    new Class[] {JMeterGUIComponent.class, TestBean.class }

在 getSearchPaths() 在这个方法中,它寻找的路径是/lib/ext,所以要确保你开发的插件的 jar 存在与这个路径中。

public static String[] getSearchPaths() {
        String p = JMeterUtils.getPropDefault("search_paths", null); // $NON-NLS-1$
        String[] result = new String[1];

        if (p != null) {
            String[] paths = p.split(";"); // $NON-NLS-1$
            result = new String[paths.length + 1];
            System.arraycopy(paths, 0, result, 1, paths.length);
        }
        result[0] = getJMeterHome() + "/lib/ext"; // $NON-NLS-1$
        return result;
    }

jmeter 提供了 example。src/examples,所以在写之前 可以多参考一下

  1. 在 src/components/sampler/gui 下新建 SmaplerGui,继承 AbstractSamplerGui 类。 ```java package org.apache.jmeter.sampler.gui;

import com.btcc.fix.TestUser;
import net.sourceforge.jdatepicker.JDateComponentFactory;
import net.sourceforge.jdatepicker.JDatePanel;
import net.sourceforge.jdatepicker.impl.UtilDateModel;
import org.apache.jmeter.gui.util.HorizontalPanel;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.sampler.FixSmapler;
import org.apache.jmeter.sampler.MessageProvider;
import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import quickfix.btcc.Message;

import javax.swing.;
import java.awt.
;
import java.awt.event.*;
import java.text.SimpleDateFormat;
import java.util.Date;

import static javax.swing.BorderFactory.createTitledBorder;

/**

  • Created by sara on 12/10/2016.
    */
    public class FixSmaplerGui extends AbstractSamplerGui {

    private static final Logger log = LoggingManager.getLoggerForClass();

    // 布局定义
    private JTextField requestNameText = null;
    private JTextField hostText = null;
    private JTextField portText = null;
    private JCheckBox sslCheckBox = null;

    public FixSmaplerGui(){
    super();
    init();
    }

    //用这个方法将 fixSmapler 的数据设置到你的 gui 中
    @Override
    public void configure(TestElement element) {

    super.configure(element);
    FixSmapler fixSmapler = (FixSmapler) element;
    hostText.setText(fixSmapler.getHost());
    portText.setText(fixSmapler.getPort());
    sslCheckBox.setSelected(fixSmapler.getSsl());

    }

    private void init() {
    setLayout(new BorderLayout(0, 5));
    setBorder(makeBorder());
    add(createDataPanel(),BorderLayout.CENTER);
    }

    // 这个方法是用来创建你自己的元素布局的
    private JPanel createDataPanel(){
    final JPanel settingPanel = new VerticalPanel(5, 0);
    settingPanel.setBorder(makeBorder());
    settingPanel.add(makeTitlePanel());

    // host
    JPanel hostPanel = new HorizontalPanel();
    hostPanel.setBorder(createTitledBorder("Host/port"));

    JLabel hostLable = new JLabel("Host:");
    this.hostText = new JTextField(15);
    hostLable.setLabelFor(hostText);
    hostPanel.add(hostLable);
    hostPanel.add(hostText);

    JLabel portLable = new JLabel("Port:");
    portText = new JTextField(15);
    portLable.setLabelFor(portText);
    hostPanel.add(portLable);
    hostPanel.add(portText);

    JLabel sslLabel = new JLabel("SSL");
    sslCheckBox = new JCheckBox();
    sslLabel.setLabelFor(sslCheckBox);
    hostPanel.add(sslLabel);
    hostPanel.add(sslCheckBox);
    settingPanel.add(hostPanel);
    return settingPanel;

    }

    // 创建新的 sampler。并且将它传给你创建的 modifyTestElement(TestElement) 方法。
    @Override
    public TestElement createTestElement() {

    TestElement sampler = new FixSmapler();
    modifyTestElement(sampler);
    return sampler;
    }

    // 这个方法应该返回代表的 component 的 title/name 的名字。fix_sampler_title 必须被写进 jmeter 的 messages.properties 文件中。
    @Override
    public String getLabelResource() {
    // TODO Auto-generated method stub
    return "fix_sampler_title"; // $NON-NLS-1$
    }

    // 这个方法是用来将数据从你的 gui 传到 TestElement.这个方法和 configure() 方法在逻辑上是相反的
    @Override
    public void modifyTestElement(TestElement element) {
    super.configureTestElement(element);
    FixSmapler smapler = (FixSmapler) element;
    smapler.setHost(hostText.getText());
    smapler.setPort(portText.getText());
    smapler.setSsl(sslCheckBox.isSelected());

    }

    // 这个方法是用来在你创建新的 sampler 时,清除数据的。
    @Override
    public void clearGui() {
    super.clearGui();
    hostText.setText("");
    portText.setText("");
    sslCheckBox.setSelected(false);

    }
    }

当是在gui上创建一个sampler,其调用的顺序是**clearGui()->createTestElement()->modifyTestElement()->configure()**

 2. 在src/components/sampler 下新建Smapler,继承AbstractSampler 类。
```java
import com.btcc.fix.FixClient;
import com.btcc.fix.TestClient;
import org.apache.jmeter.samplers.AbstractSampler;
import org.apache.jmeter.samplers.Entry;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.testelement.property.IntegerProperty;
import org.apache.jmeter.testelement.property.StringProperty;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * Created by sara on 12/10/2016.
 */
public class FixSmapler extends AbstractSampler {

    private static final Logger log = LoggingManager.getLoggerForClass();


    private static final String HOST = "Fix.host"; //$NON-NLS-1$
    private static final String PORT = "Fix.port"; //$NON-NLS-1$
    private static final String SSL = "Fix.ssl"; //$NON-NLS-1$

    private static TestClient client = null;
    private static Message request  = null;


    private static AtomicInteger classCount = new AtomicInteger(0); // keep track of classes created

    public FixSmapler() {
       classCount.incrementAndGet();
        trace("FixSmapler()");
    }

    // 这个方法主要是用来执行和收集执行后的result数据的
    @Override
    public SampleResult sample(Entry entry) {
        trace("sample()");
        SampleResult res = new SampleResult();
        boolean isOK = false;

        TestClient c = getClient();
        // 设置结果的名称
        res.setSampleLabel(getTitle());

        // 设置request数据
        res.setSamplerData(request.toString());
        res.sampleStart();

        final CountDownLatch responseLatch = new CountDownLatch(1);

        StringBuffer sb = new StringBuffer();
        FixClient.Callback callback = new FixClient.Callback() {
            @Override
            public void onMessage(quickfix.Message message) {

                String[] fds = message.toString().split("\\001");

                for(String fd:fds){
                    sb.append(fd).append("\r\n");
                }

                responseLatch.countDown();
            }
        };

        try{

            int count = 0;
            while (!c.connect()){
                Thread.sleep(1000);

                count++;

                if(count >=10){
                    throw new Exception("Fix server can't connect");
                }
            }

            c.send(request,callback);
            responseLatch.await();
            //设置response数据
            res.setResponseMessage(sb.toString());
            byte[] bytes = sb.toString().getBytes();
            res.setResponseData(bytes);
            res.setDataType(SampleResult.TEXT);
            res.setResponseCodeOK();
            res.setResponseMessage("OK");
            isOK = true;
        }catch (Exception e){
            log.error("send fail: ".concat(e.toString()));
            res.setResponseCode("500");// $NON-NLS-1$
            res.setResponseData(e.toString());
        }finally {
            c.removeCallback(callback);
            c.disconnect();
        }

        res.sampleEnd();

        res.setSuccessful(isOK);
        return  res;
    }
  1. 执行 ant install
    安装过后,你直接运行 NewDriver 和 /bin/jmeter 都是可以的。

  2. 截图fixSampler

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 12 条回复 时间 点赞

最好说一下二次开发要解决的问题是啥,表示没看懂

#1 楼 @DoctorQ 就是研究一下。。。

挺好,后面我这边工作也要基于 jmeter 开发些插件

—— 来自 TesterHome 官方 安卓客户端

请问怎么发短信息给你? appium 那个项目有个问题问下。。。。 import readConfig as readConfig 这是什么? 运行报错 No module named 'readConfig';

#4 楼 @jojotester 可以在官方 qq 群里找到我。

#5 楼 @tongshanshanshan 官方群不能加了。。。

#7 楼 @tongshanshanshan QQ 开了限制查找不到; 方便加我下 2428518265。

👍 社区牛人好多

能加下您 qq 或者微信吗,我有问题想问下?

sampler 怎么回去 gui 填写的数据?

解决了什么问题。哈哈

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up