欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Programming with JMeter-- Sampler and Listeners

程序员文章站 2022-03-02 11:45:42
...

        完成JMeterEngine, ThreadGroup 和 JMeterThread的整合,外围的设施差不多搞定大半了,接下来是把具体的测试逻辑整合进来,既然是框架层面的,需要通用一点,方便客户端自己定义测试逻辑。不过在此之前,还需要研究下Sampler和Listeners,这样对JMeter的整个流程会有全局的把握。先看下图:

Programming with JMeter-- Sampler and Listeners
            
    
    博客分类: JMeter SamplerSampleListenerTestStateListener        SampleListener和TestStateListener作为很重要的两个Listener贯穿了JMeter整个test的生命周期。

       先看TestStateListener,两个方法名就能让我们猜到作用:

1) 在测试开始时候调用 testStarted

2)    在测试结束的时候调用testEnded    

      

       这些生命周期函数的调用是在什么地方呢?这个又要回到JMeterEngine了,看StandardJMeterEngine#run()方法,在启动ThreadGroup之前以及整个测试结束之后,代码是这样的:

 

public void run(){
        。。。。。。
        SearchByClass<TestStateListener> testListeners = new SearchByClass<TestStateListener>(TestStateListener.class); // TL - S&E
        test.traverse(testListeners);

        // Merge in any additional test listeners
        // currently only used by the function parser
        testListeners.getSearchResults().addAll(testList);
        testList.clear(); // no longer needed

        if (!startListenersLater ) { notifyTestListenersOfStart(testListeners); }
        test.traverse(new TurnElementsOn());
        if (startListenersLater) { notifyTestListenersOfStart(testListeners); }
        。。。。。。

        ////此处省略 n 行代码
 
        。。。。。。

       notifyTestListenersOfEnd(testListeners);        
}

 

 

          代码解释如下:

          1) 从HashTree中找出所有TestStateListener类型

          2) 在测试正式开始之前,触发这些TestStateListener,notifyTestListenersOfStart(testListeners)这个方法中会调用testListener#testStarted方法

          3) 在测试执行完之后,再触发这些TestStateListener,notifyTestListenersOfEnd(testListeners)这个方法会调用testListener#testEnded方法

 

           理解了这些,来看看具体三种我们会用到TestStateListener(如上面类图):TestPlan,JavaSampler,RresultCollector,看看他们的testStarted()分别做些什么

 1. TestPlan:主要设置一些文件路径,方便查找config, log等文件,不需要再去干预。

 2. JavaSampler: 主要是加载client类,类名是根据HashTree中的配置数据。这个比较重要,因为这里是整合测试逻辑的地方,看下代码

               

@Override
    public void testStarted() {
        log.debug(whoAmI() + "\ttestStarted");
        initClass();
    }

private void initClass() {
        String name = getClassname().trim();
        try {
            javaClass = Class.forName(name, false, Thread.currentThread().getContextClassLoader());
            Method method = javaClass.getMethod("teardownTest", new Class[]{JavaSamplerContext.class});
            isToBeRegistered = !method.getDeclaringClass().equals(AbstractJavaSamplerClient.class);
            log.info("Created class: " + name + ". Uses tearDownTest: " + isToBeRegistered);
        } catch (Exception e) {
            log.error(whoAmI() + "\tException initialising: " + name, e);
        }
        
    }

/**
     * Gets the Classname attribute of the JavaConfig object
     *
     * @return the Classname value
     */
    public String getClassname() {
        return getPropertyAsString(CLASSNAME);
    }
 3) ResultCollector:主要是初始化File Output,做好写测试结果的准备。代码如下:

 

   

@Override
    public void testStarted(String host) {
        synchronized(LOCK){
            instanceCount++;
            try {
                initializeFileOutput();
                if (getVisualizer() != null) {
                    this.isStats = getVisualizer().isStats();
                }
            } catch (Exception e) {
                log.error("", e);
            }
        }
        inTest = true;

        if(summariser != null) {
            summariser.testStarted(host);
        }
    }
       
          so far so good, TestStarted的准备已经完成。按照流程,我们要回到JMeterThread#run()方法了,此方法在上一篇有详述,这里只看相关的代码:      
/////In JmeterThread

@Override
    public void run() {
......
process_sampler(sam, null, threadContext);
......
}

private SampleResult process_sampler(Sampler current, Sampler parent, JMeterContext threadContext){
......
      SampleResult result = sampler.sample(null);
      。。。。。。
      List<SampleListener> sampleListeners = getSampleListeners(pack, transactionPack, transactionSampler);
      notifyListeners(sampleListeners, result);
......
}

private void notifyListeners(List<SampleListener> listeners, SampleResult result) {
        SampleEvent event = new SampleEvent(result, threadGroup.getName(), threadVars);
        notifier.notifyListeners(event, listeners);
}
     如上代码,每一个JMeterThread在运行时,当一个Sampler#sample触发得到一个SampleResult,那么回到JavaSampler,前文所述在TestStarted触发时,JavaSampler作为TestStateListener已经加载了Client类,那么他到底有什么用呢?现在可以揭晓了:     
////JavaSampler
    public SampleResult sample(Entry entry) {        
        Arguments args = getArguments();
        args.addArgument(TestElement.NAME, getName()); // Allow Sampler access
                                                        // to test element name
        context = new JavaSamplerContext(args);
        if (javaClient == null) {
            log.debug(whoAmI() + "\tCreating Java Client");
            javaClient = createJavaClient();
            javaClient.setupTest(context);
        }

        SampleResult result = javaClient.runTest(context);

        // Only set the default label if it has not been set
        if (result != null && result.getSampleLabel().length() == 0) {
            result.setSampleLabel(getName());
        }

        return result;
    }

private JavaSamplerClient createJavaClient() {
        if (javaClass == null) { // failed to initialise the class
            return new ErrorSamplerClient();
        }
        JavaSamplerClient client;
        try {
            client = (JavaSamplerClient) javaClass.newInstance();

            if (log.isDebugEnabled()) {
                log.debug(whoAmI() + "\tCreated:\t" + getClassname() + "@"
                        + Integer.toHexString(client.hashCode()));
            }
            
            if(isToBeRegistered) {
                TEAR_DOWN_SET.add(this);
            }
        } catch (Exception e) {
            log.error(whoAmI() + "\tException creating: " + getClassname(), e);
            client = new ErrorSamplerClient();
        }
        return client;
    }
        其实没什么神奇的,读代码可以得到几点信息:
1) 通过HashTree中配置的classname,反射调用构造方法得到一个实例
2)该类是客户端提供的,而且必须是JavaSamplerClient类型
3)每次Sample时,JavaSamplerClient#runTest会被执行
         这些扩展点为进一步集成提供了很好的线索,这里不展开,继续沿着JMeterThread#run思路,sampleOccurred产生一个SampleResult之后,所有的SampleListener都会被触发,即SampleListener#sampleOccurred(event)方法会被调用,再看本文开头的类图,也就是说ResultCollector#sampleOccurred(event)会执行:    
@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);
        }
    }
         可以看到,ResultCollector会去保存xml或者csv格式的测试结果文件,还会触发汇总Summariser.sampleOccurred(event),另外还有GUI上的显示等等,这些其实已经不是太重要了,因为实际测试中很可能是一个集群的环境,测试结果可能需要通过Remote写到*测试机上。也就是说实现自己的ResultCollector不可避免的。
        所有的JMeterThread结束之后,回到StandardJMeterEngine#run()最后一行notifyTestListenersOfEnd(testListeners),TestStateListener(ResultCollector, JavaSampler, TestPlan)的testEnded又被触发:
1)TestPlan#testEnded(): 关闭测试开始时候打开的文件
2)ResultCollector#testEnded():写文件结束符(xml的话必须谢结束的tag </...>),关闭stream,file等
3)若实现了teardownTest方法,则调用JavaSampleClient#testdownTest(JavaSamplerContext)
        到此,JMeter的大致的生命周期介绍完毕,当然不是全部的生命周期都涵盖在这篇文章里了,比如在上一篇讲到过,JMeterThread运行结束退出前有个回调JMeterThreadMonitor#threadFinished(JMeterThread)在这里没有提及,这个JMeterThreadMonitor接口的实现类主要就是ThreadGroup。可以看前几篇文章去了解。

 

  • Programming with JMeter-- Sampler and Listeners
            
    
    博客分类: JMeter SamplerSampleListenerTestStateListener 
  • 大小: 9.4 KB
  • Programming with JMeter-- Sampler and Listeners
            
    
    博客分类: JMeter SamplerSampleListenerTestStateListener 
  • 大小: 52.1 KB