JMeter源码解析之结果收集器

news2024/11/26 16:27:40

目录

前言:

一、JMeter结果收集器概述

二、单机模式

三、分布式模式

四、总结


前言:

JMeter是一款著名的开源性能测试工具,JMeter提供了多种结果收集器,以帮助用户对性能测试结果数据进行收集、整理和分析。

一、JMeter结果收集器概述

JMeter是在压力领域中最常见的性能测试工具,由于其开源的特点,受到广大测试和开发同学的青睐。但是,在实际应用过程中,JMeter存在的一些性能瓶颈也凸显出来,经常会遇到大并发下压不上去的情况。笔者通过深入分析其源码实现,找到JMeter存在的瓶颈问题及根本原因,为以后更好地使用工具提供一些思路。

结果收集器:在JMeter中担任报告数据收集的重任,无论是单机模式还是master-slave模式,每一个请求的结果都是通过相应的结果收集器进行数据采集的。在单机模式下用Result Collector这个监听器去采集,在分布式(master-slave)场景下通过配RemoteSampleListenerWrapper下的指定sender进行收集,具体配置jmeter.property文件的mode属性和队列长度实现。下面我们以当前最新的JMeter 5.5版本的源代码为例详细介绍下单机模式和分布式模式下结果收集器的工作原理。

二、单机模式

1、初始化

在命令行模式下,JMeter会根据用户的logfile配置选择是否添加Result Collector,一般在实际测试的时候,我们都是需要有详细统计报告生成的,所以都会添加Result Collector,收集器放在了整个hashtree的第一个节点,代码如下:

 void runNonGui(String testFile, String logFile, boolean remoteStart, String remoteHostsString, boolean generateReportDashboard){

 ....

 ResultCollector resultCollector = null;

   if (logFile != null) {

     resultCollector = new ResultCollector(summariser);

     resultCollector.setFilename(logFile);

     clonedTree.add(clonedTree.getArray()[0], resultCollector);

     }

   else {

     // only add Summariser if it can not be shared with the ResultCollector

   if (summariser != null) {

      clonedTree.add(clonedTree.getArray()[0], summariser);

      }

      }

 ....



 }

2、加载流程

添加完结果收集器后,执行脚本过程中,JMeter会根据jmx的编排,按照如下的执行顺序进行调用:

每一个线程都是按照以上的顺序循环反复执行,直到压测停止。具体代码如下(相应的关键点已增加注释):

private void executeSamplePackage(Sampler current,

      TransactionSampler transactionSampler,

      SamplePackage transactionPack,

      JMeterContext threadContext) {

  threadContext.setCurrentSampler(current);

  // Get the sampler ready to sample

  SamplePackage pack = compiler.configureSampler(current);

  runPreProcessors(pack.getPreProcessors());//运行前置处理器

  // Hack: save the package for any transaction controllers

  threadVars.putObject(PACKAGE_OBJECT, pack);

  delay(pack.getTimers());//定时器timer

  SampleResult result = null;

  if (running) {

       Sampler sampler = pack.getSampler();

       result = doSampling(threadContext, sampler);

   }

   // If we got any results, then perform processing on the result

   if (result != null) {

   if (!result.isIgnore()) {

          ...               

   runPostProcessors(pack.getPostProcessors());//运行后置处理器

   checkAssertions(pack.getAssertions(), result, threadContext);//运行断言处理器

            // PostProcessors can call setIgnore, so reevaluate here

            if (!result.isIgnore()) {

            // Do not send subsamples to listeners which receive the transaction sample

            List<SampleListener> sampleListeners = getSampleListeners(pack, transactionPack, transactionSampler);

            notifyListeners(sampleListeners, result);//执行监听器,此处为执行报告收集器的sampleOccurred方法

            }

            compiler.done(pack);

            ...

    }

收集器Result Collector执行的具体代码:

@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

                   CSVSaveService.saveSampleResult(event, out);

               }

          } catch (Exception err) {

              log.error("Error trying to record a sample", err); // should throw exception back to caller

           }

      }

  }

   if(summariser != null) {

       summariser.sampleOccurred(event);

   }

}

以上主要实现了将每个请求的结果数据存储到日志文件中(CSV /XML),为后续的报告生成提供数据文件。

3、性能瓶颈分析

从以上的流程不难看出,由于每个线程的每个请求后都会频繁调用Result Collector的sample Occurred方法,即会频繁读写文件,有可能导致IO瓶颈。一旦存储的速度下降,必然导致线程循环发包的速度下降,从而导致压不上去的情况出现。所以单机模式下不建议设置超过200以上的并发,若非必须,尽量关闭日志采集和html报告生成,以免报告置信度存在问题。

三、分布式模式

为了应对单机的各种瓶颈问题,JMeter采用了分布式(master-slave)模式。加载执行流程与单机基本一致,不再赘述,区别在于监听器换成了Remote Sample ListenerImpl收集器。

1、发送模式指定方法

下面我们重点看下Remote Sample ListenerImpl监听器的代码:

@Override

public void processBatch(List<SampleEvent> samples) {

    if (samples != null && sampleListener != null) {

        for (SampleEvent e : samples) {

            sampleListener.sampleOccurred(e);

        }

    }

}

@Override

public void sampleOccurred(SampleEvent e) {

    if (sampleListener != null) {

        sampleListener.sampleOccurred(e);

    }

}

从以上代码可以看出,这个监听器里又调用了sample Listener的sample Occurred方法,而sample Listener是通过用户在jmeter.property文件中指定的。

2、AsynchSampleSender源码解析

下面我们以Asynch Sample Sender为例进行源码详细介绍:

public class AsynchSampleSender extends AbstractSampleSender implements Serializable {

       protected Object readResolve() throws ObjectStreamException{

        int capacity = getCapacity();

        log.info("Using batch queue size (asynch.batch.queue.size): {}", capacity); // server log file

        queue = new ArrayBlockingQueue<>(capacity);

        Worker worker = new Worker(queue, listener);

        worker.setDaemon(true);

        worker.start();

        return this;

    }

@Override

public void testEnded(String host)

    log.debug("Test Ended on {}", host);

    try {

        listener.testEnded(host);

        queue.put(FINAL_EVENT);

    } catch (Exception ex) {

        log.warn("testEnded(host)", ex);

    }

    if (queueWaits > 0) {

        log.info("QueueWaits: {}; QueueWaitTime: {} (nanoseconds)", queueWaits, queueWaitTime);

        }

    }

 @Override

public void sampleOccurred(SampleEvent e)

    try {

        if (!queue.offer(e)){ // we failed to add the element first time

            queueWaits++;

            long t1 = System.nanoTime();

            queue.put(e);

            long t2 = System.nanoTime();

            queueWaitTime += t2-t1;

        }

    } catch (Exception err) {

        log.error("sampleOccurred; failed to queue the sample", err);

    }

}

private static class Worker extends Thread {

    @Override

    public void run()

        try {

            boolean eof = false;

            while (!eof) {

                List<SampleEvent> l = new ArrayList<>();

                SampleEvent e = queue.take();

                // try to process as many as possible

                // The == comparison is not an error

                while (!(eof = e == FINAL_EVENT) && e != null) {

                     l.add(e);

                     e = queue.poll(); // returns null if nothing on queue currently

                 }

                int size = l.size();

                if (size > 0) {

                    try {

                       listener.processBatch(l);

                    } catch (RemoteException err) {

                        if (err.getCause() instanceof java.net.ConnectException){

                            throw new JMeterError("Could not return sample",err);

                        }

                        log.error("Failed to return sample", err);

                    }

                 }

            }

        } catch (InterruptedException e) {

            Thread.currentThread().interrupt();

            }

        log.debug("Worker ended");

        }

    }

}

从以上代码可以看出,Asynch SampleSender的sample Occurred方法里只进行入列的操作,而采集上报工作是启动了一个work线程实现的,相当于异步处理所有请求数据。这样设计不会阻塞发包的流程,性能上要优于单机模式。但是,在一定情况下,也是会出现性能瓶颈的。

这个队列采用的是Array Blocking Queue(阻塞队列),这个队列有如下特点:

·Array Blocking Queue是有界的初始化必须指定大小,队列满了后,无法入列。

·Array Blocking Queue实现的队列中的锁是没有分离的,即添加操作和移除操作采用的同一个Reenter Lock锁。

3、性能瓶颈分析

瓶颈点一:队列大小问题

当我们实际压测过程中,如果队列大小(asynch.batch.queue.size)设置过小,入列速度大于出列速度,就会导致队列满而阻塞整个发压流程,而如果队列设置过大,一旦请求的包体比较大,很容易造成内存溢出。

瓶颈点二:单一锁问题

在压测过程中,入列出列是非常频繁的,而同一个Reenter Lock锁也可能造成入列和出列过程中,因无法获得锁而入列或者出列延迟,继而影响发压效率。

四、总结

JMeter因其完善的社区和开源特点,在日常压测中可广泛使用。JMeter适合进行小规模的压测。但是在大规模的压测过程中,受本地机器性能、带宽等限制,不宜进行单机压测,可以使用JMeter的master-slave的方式进行分布式压测。但是需提前设置好结果收集器和队列的大小,并进行预先演练评估出上限qps,防止出现压不上去的情况。此外,master-slave通信方式是远程RMI的双向通信方式,连接数过多也会造成master的瓶颈出现,需要做好量级的提前评估。

 作为一位过来人也是希望大家少走一些弯路,希望能对你带来帮助。(WEB自动化测试、app自动化测试、接口自动化测试、持续集成、自动化测试开发、大厂面试真题、简历模板等等),相信能使你更好的进步!

留【自动化测试】即可【自动化测试交流】:574737577(备注ccc)icon-default.png?t=N4P3http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=8hUKwUHn9HhVpR8qWhqfT2u-kU-3hpsF&authKey=47BBG1nwHVOka38EQeJevQFCP%2BeVEf%2Bpd8QqotS1%2FqyJdrGAo1A6%2BfS9ef3wJij2&noverify=0&group_code=574737577

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/651329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mysql数据库初体验及管理

Mysql数据库初体验及管理 一、数据库相关概念1.数据库的组成2.数据库管理系统3.数据库系统 二、数据库的发展1.第一代数据库2.第二代数据库3.第三代数据库 三、主流的数据库介绍四、关系型数据库1、介绍2、实体3、关系4、属性非关系型数据库 五、Mysql数据库管理1、库和表2、常…

Excel 2021入门指南:详细解读常用功能

软件安装&#xff1a;办公神器office2021安装教程&#xff0c;让你快速上手_正经人_____的博客-CSDN博客 一、 新建工作表 打开Excel 2021后&#xff0c;可以看到左上角的“文件”选项&#xff0c;在弹出的菜单中选择“新建”选项&#xff0c;然后可以选择使用空白工作表或者…

工具-自动获取/校对XpathHelper/XpathHelperPlus/XPathHelperWizard插件轻松配置xpath规则

目录 一、xpath插件功能对比1.1、应用的范围1.2、不依赖插件&#xff0c;依赖谷歌开发者工具1.3、XpathHelper/XpathHelperPlus/XPathHelperWizard插件功能特点 二、安装XpathHelper插件以及用法2.1、安装XpathHelperPlus插件2.2、XpathHelperPlus插件使用方法 三、安装XPathHe…

自然语言处理: 第二章Word2Vec

一. 理论基础 维度很高(与语料库有关)&#xff0c;计算复杂稀疏性&#xff0c;浪费计算效率&#xff0c;只有一个元素是1 其他都是0缺乏语义信息&#xff0c;无法衡量语义相似度无法处理未知单词 而在One-Hot的基础上&#xff0c;Word2Vec 是一种分布式表达字/词的方式&#x…

Java|注解之定义注解

Java语言使用interface语法来定义注解&#xff08;Annotation&#xff09;&#xff0c;它的格式如下&#xff1a; public interface Report {int type() default 211;String level() default "211";String value() default "211"; } 注解的参数类似无参数…

华为OD机试真题 JavaScript 实现【比赛评分】【2023 B卷 100分】,附详细解题思路

一、题目描述 一个有N个选手参加比赛&#xff0c;选手编号为1~N&#xff08;3<N<100&#xff09;&#xff0c;有M&#xff08;3<M<10&#xff09;个评委对选手进行打分。打分规则为每个评委对选手打分&#xff0c;最高分10分&#xff0c;最低分1分。 请计算得分最…

Android 音视频开发核心知识点笔记整合

很多开发者都知道Android音视频开发这个概念&#xff0c;音视频开发不仅需要掌握图像、音频、视频的基础知识&#xff0c;并且还需要掌握如何对它们进行采集、渲染、处理、传输等一系列的开发和应用&#xff0c;因此&#xff0c;音视频开发是一门涉及到很多内容的领域。 随着5G…

开战在即!与全球伙伴一起打造你的数据应用,TiDB Future App Hackathon 2023 来啦!

2023 TiDB Future App Hackathon 来啦&#xff01;本届 Hackathon 的主题为&#xff1a;Code, Innovate & Build Amazing Data Applications —— 释放你的创造力、构建突破性的应用、在全球范围内寻找你的队友、体验最新最 in 的 Serverless 技术&#xff0c;更有 总计 $3…

马原第一章复习1.

一.物质的存在方式 《德法年鉴》 完成从唯物到唯心 从革命主义等到共产主义的过度 为创立马克思理论提供了根本前提《德意志形态》 首次阐述了历史唯物主义的基本观点《共产党宣言》标志着马克思主义的公开问世 也是第一个无产阶级政党的党纲《资本论》阐述剩余价值学说 解释生…

【推荐】Oracle Live SQL——在线 Oracle SQL 测试工具

最近回答了几个 CSDN “学习”功能里“问答”区的一些专业相关问题&#xff0c;回答过程中采用严谨的方式&#xff0c;在 Oracle Live SQL 上进行验证测试。这个很好用的 Oracle APEX 应用我使用好几年了&#xff0c;虽然近年来已转行 MySQL 和国产数据库领域&#xff0c;但仍然…

链表与顺序表的区别以及扩展计算机硬件的存储体系

好久没有更新文章了&#xff0c;在忙学校的事情时我还是比较怀念大家一直以来对我的关注和鼓励&#xff0c;接下来我会继续更新数据结构相关的文章&#xff0c;也请大家多多支持&#xff0c;十分感谢。正文来了&#xff1a; 首先说明一点&#xff0c;我在举例和比较时所使用的是…

【2023,学点儿新Java-02】计算机硬件与软件 | CPU、内存、硬盘概览 | 科学使用键盘——“指法” | 软件——计算机的灵魂 | 人机交互方式

前情回顾&#xff1a; 【2023&#xff0c;学点儿新Java-01】从查看本机 jdk版本 开始 | Java基础全程脉络图、Java工程师全程技术路线、Java职业晋升路线图 我们见到的太阳 是八分钟前的太阳&#xff0c;见到的月亮 是一点三秒之前的月亮&#xff0c;见到一英里之外的建筑&…

【Docker 安装 Zipkin】—— 每天一点小知识

&#x1f4a7; D o c k e r 安装 Z i p k i n \color{#FF1493}{Docker 安装 Zipkin} Docker安装Zipkin&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》…

解决vue打包一次部署到不同的服务器的问题

1. 问题描述 在工作的时候&#xff0c;往往碰到同一套vue前端代码程序需要部署到很多的服务器上&#xff0c;每次更改完程序都需要打包部署到各个服务器上&#xff0c;因为每个服务器的访问地址和端口都不一样&#xff0c;如果用的若依自带的框架&#xff0c;需要每次都需要打…

Spring Security --- 自定义登录逻辑

目录 UserDetailsService详解 返回值 方法参数 异常 PasswordEncoder密码解析器详解 接口介绍 内置解析器介绍 BCryptPasswordEncoder简介 代码演示 自定义登录逻辑 编写配置类 自定义逻辑 UserDetailsService详解 当什么也没有配置的时候&#xff0c;账号和密码是…

Vue3:计算属性、监听器

computed 计算属性 计算属性是指 基于现有状态派生 (演变) 出新的状态&#xff0c;现有状态发生变化&#xff0c;派生状态重新计算。 computed 接收回调函数作为参数&#xff0c;基于回调函数中使用的响应式数据进行计算属性的创建&#xff0c;回调函数的返回值就是基于现有状态…

软件测试什么样的技术栈才能进入大厂

我们知道&#xff0c;能在一线大厂工作是大多数人的目标&#xff0c;不仅薪酬高&#xff0c;技能提升快&#xff0c;而且能得到公司影响力背书&#xff0c;将来就算跳槽也能带来光环加持。 最近疫情的影响&#xff0c;网上也爆出了一些裁员新闻&#xff0c;可以说这个疫情确实…

【深入浅出密码学】RSA

RSA密码体制 引言&#xff1a; RSA加密的本意并不是为了取代对称密码&#xff0c;而且它比诸如AES的密码要慢很多&#xff0c;因为RSA当中涉及许多数学计算&#xff0c;RSA通常和类似AES的对称密码一起使用&#xff0c;真正用来加密大量数据的是对称密码。而RSA主要保护对称密…

Linux0.11内核源码解析-block_dev.c

目录 block_dev.c 文件的作用 int block_write(int dev, long * pos, char * buf, int count) block_dev.c 文件的作用 block_dev.c 文件的作用 block_dev.c 文件就包含两个函数&#xff0c;分别是block_read和block_write函数&#xff0c;提供给read和write系统调用 块读写…

STM32开发踩坑——基于CubeMx+Gcc移植正点原子3.5‘TFTLCD(开发环境:正点F103精英版+3.5‘TFTLCD)

成立这个专栏的目的是&#xff0c;记录自己嵌入式开发遇到的问题&#xff0c;与成功的解决方法&#xff0c;方便自己回顾。 最近在学习王维波老师的《STM32Cube高效开发教程》&#xff0c;王老师移植的是普中科技的驱动&#xff0c;而我手动移植了一下正点原子的lcd驱动&#…