前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JMeter5.1核心类TestCompiler源码分析

JMeter5.1核心类TestCompiler源码分析

原创
作者头像
天堂小说
发布2021-12-11 15:53:17
6510
发布2021-12-11 15:53:17
举报
文章被收录于专栏:JMeter源码分析JMeter源码分析

概述

TestCompiler类继承HashTreeTraverser类,重写addNode和subtractNode方法来解析jmx文件中的控制器LoopController、Sampler、前后置处理器、配置元件、定时器、断言、提取器等。

源码分析

构造函数

获取HashTree对象,这里存放的是ThreadGroup对象及其子节点列表

代码语言:txt
复制
    public TestCompiler(HashTree testTree) {
        this.testTree = testTree;
    }

主要变量

代码语言:txt
复制
    // 对象对
    private static final Set<ObjectPair> PAIRING = new HashSet<>();
    
    // 栈,存放线程组、Controller、Sampler、断言等对象
    private final LinkedList<TestElement> stack = new LinkedList<>();

    private final Map<Sampler, SamplePackage> samplerConfigMap = new HashMap<>();

    private final Map<TransactionController, SamplePackage> transactionControllerConfigMap =
            new HashMap<>();

    private final HashTree testTree;

主要方法

initialize

在测试运行开始时,通过调用StandardJmeterEngine类来清除配对集

代码语言:txt
复制
    public static void initialize() {
        // synch is probably not needed as only called before run starts
        synchronized (PAIRING) {
            PAIRING.clear();
        }
    }

addNode

被HashTree的traverse和traverseInTo方法调用,通过函数递归,将node对象添加到栈中。

以jmx文件的线程组及其子节点为例:

  • ThreadGroup
    • Arguments
    • LoopController
    • HTTPSamplerProxy
      • RegexExtractor

首先将ThreadGroup添加到stack中,然后是Arguments,直到最后将RegexExtractor也添加到stack中

由于存储空间用的是栈,所以栈顶节点是RegexExtractor,栈底节点是ThreadGroup。

代码语言:txt
复制
    public void addNode(Object node, HashTree subTree) {
        stack.addLast((TestElement) node);
    }

subtractNode

被HashTree的traverseInTo方法调用,当执行完addNode()方法后,执行subtractNode方法。

代码语言:txt
复制
    public void subtractNode() {
        if (log.isDebugEnabled()) {
            log.debug("Subtracting node, stack size = {}", stack.size());
        }
       
        // 获取栈顶节点(后进先出)
        TestElement child = stack.getLast();
        trackIterationListeners(stack);

        //判断是否为Sampler
        if (child instanceof Sampler) {
            // 获取sampler相关的配置元件,监听器,前后置处理器等对象
            saveSamplerConfigs((Sampler) child);
        }
        // 好像用不到,跳过
        else if(child instanceof TransactionController) {
        	System.out.println("this is a transactionController.");
            saveTransactionControllerConfigs((TransactionController) child);
        }

        // 移除栈顶节点,即child节点
        stack.removeLast();
        // 判断栈是否为空
        if (!stack.isEmpty()) {
            // 获取栈顶节点,即parent节点
            TestElement parent = stack.getLast();
            
            // 对象对是否已存在对象的标志
            boolean duplicate = false;
            // Bug 53750: this condition used to be in ObjectPair#addTestElements()
            // 条件1:parent节点为controller类型
            // 条件2:child节点为Sampler类型或者cotroller类型
            if (parent instanceof Controller && (child instanceof Sampler || child instanceof Controller)) {
                // 判断类型是否为TestCompilerHelper
                // 这里threadGroup和GenericController都继承TestCompilerHelper
                if (parent instanceof TestCompilerHelper) {
                    TestCompilerHelper te = (TestCompilerHelper) parent;
                    // 将child添加到parent的subControllersAndSamplers列表中
                    duplicate = !te.addTestElementOnce(child);
                } else { // this is only possible for 3rd party controllers by default
                    ObjectPair pair = new ObjectPair(child, parent);
                    synchronized (PAIRING) {// Called from multiple threads
                        if (!PAIRING.contains(pair)) {
                            parent.addTestElement(child);
                            PAIRING.add(pair);
                        } else {
                            duplicate = true;
                        }
                    }
                }
            }
            if (duplicate) {
                if (log.isWarnEnabled()) {
                    log.warn("Unexpected duplicate for {} and {}", parent.getClass(), child.getClass());
                }
            }
        }
    }

saveSamplerConfigs

获取sampler的公共的配置元件、监听器、定时器,循环控制器等组件节点,也包括其下的所有子组件节点(前后置处理器,断言,提取器等)

代码语言:txt
复制
    private void saveSamplerConfigs(Sampler sam) {
        // 存放配置元件的列表
        List<ConfigTestElement> configs = new LinkedList<>();
        // 存放controller的列表
        List<Controller> controllers = new LinkedList<>();
        // 存放监听器的列表
        List<SampleListener> listeners = new LinkedList<>();
        // 存放定时器的列表
        List<Timer> timers = new LinkedList<>();
        // 存放断言的列表
        List<Assertion> assertions = new LinkedList<>();
        // 存放前置处理器的列表
        LinkedList<PostProcessor> posts = new LinkedList<>();
        // 存放后置处理器的列表
        LinkedList<PreProcessor> pres = new LinkedList<>();

         //循环遍历获取栈中的每个节点,后进先出
        for (int i = stack.size(); i > 0; i--) {
        	// 获取当前sam的所有父controller节点,比如嵌套的LoopController、IFController和ThreadGroup
            addDirectParentControllers(controllers, stack.get(i - 1));

            List<PreProcessor>  tempPre = new LinkedList<>();
            List<PostProcessor> tempPost = new LinkedList<>();
            List<Assertion> tempAssertions = new LinkedList<>();
            
            /**
             * stack.subList(0, i):获取0到i之间stack的元素列表
             * testTree.list():遍历subList列表,获取其最后一个节点的子对象列表,其实就是栈顶节点的子对象列表
             */
            for (Object item : testTree.list(stack.subList(0, i))) {
            	
            	/**
            	 * 两层for循环结合使用,获取公共的配置元件、监听器、定时器,循环控制器等组件节点,也包括当前sam下的所有子组件节点(前后置处理器,断言,提取器等)
            	 * 
            	 */
                if (item instanceof ConfigTestElement) {
                    configs.add((ConfigTestElement) item);
                }
                if (item instanceof SampleListener) {
                    listeners.add((SampleListener) item);
                }
                if (item instanceof Timer) {
                    timers.add((Timer) item);
                }
                if (item instanceof Assertion) {
                    tempAssertions.add((Assertion) item);
                }
                if (item instanceof PostProcessor) {
                    tempPost.add((PostProcessor) item);
                }
                if (item instanceof PreProcessor) {
                    tempPre.add((PreProcessor) item);
                }
            }
            assertions.addAll(0, tempAssertions);
            pres.addAll(0, tempPre);
            posts.addAll(0, tempPost);
        }

        // 存储当前sam的配置元件、监听器、定时器、断言、前后置处理器,控制器等的类
        SamplePackage pack = new SamplePackage(configs, listeners, timers, assertions,
                posts, pres, controllers);
        
        // 存储当前sam
        pack.setSampler(sam);
        // 使SamplePackage成为正在运行的版本,或使其不再是正在运行的版本。
        // 这告诉SamplePackage的每个元素它的当前状态必须是可通过调用recoverRunningVersion()检索。
        pack.setRunningVersion(true);
        // 键值对存储
        samplerConfigMap.put(sam, pack);
    }

configureSampler

根据sampler获取SamplePackage对象,主要用于提取控制器、定时器、前后置处理器,断言等

代码语言:txt
复制
    public SamplePackage configureSampler(Sampler sampler) {
        SamplePackage pack = samplerConfigMap.get(sampler);
        pack.setSampler(sampler);
        configureWithConfigElements(sampler, pack.getConfigs());
        return pack;
    }

configureWithConfigElements

将ConfigTestElement添加到Sampler中,作为Sampler的属性配置,主要作用于HTTPSamplerBase,用于添加Cookie,Header,Auth等

代码语言:txt
复制
    private void configureWithConfigElements(Sampler sam, List<ConfigTestElement> configs) {
        // 只适用于HTTPSamplerBase,用于清除header_manager列表
        sam.clearTestElementChildren();
        for (ConfigTestElement config  : configs) {
            // 配置元件的类型不是CSVDataSet和RandomVariableConfig
            if (!(config instanceof NoConfigMerge)) 
            {
                // 判断Sampler的类型是否为ConfigMergabilityIndicator
                if(sam instanceof ConfigMergabilityIndicator) {
                    // 判断ConfigTestElement的gui_class名能否在Sampler的APPLIABLE_CONFIG_CLASSES找到,找到为true,找不到为false
                    if(((ConfigMergabilityIndicator)sam).applies(config)) {
                        // 将ConfigTestElement添加到Sampler中,作为Sampler的属性配置
                        // 主要作用于HTTPSamplerBase,用于添加Cookie,Header,Auth等
                        sam.addTestElement(config);
                    }
                } else {
                    // Backward compatibility
                    sam.addTestElement(config);
                }
            }
        }
    }

addDirectParentControllers

获取当前sam的所有父controller节点,比如嵌套的LoopController、IFController和ThreadGroup等

代码语言:txt
复制
    private void addDirectParentControllers(List<Controller> controllers, TestElement maybeController) {
        if (maybeController instanceof Controller) {
            log.debug("adding controller: {} to sampler config", maybeController);
            controllers.add((Controller) maybeController);
        }
    }

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 源码分析
    • 构造函数
      • 主要变量
        • 主要方法
          • initialize
            • addNode
              • subtractNode
                • saveSamplerConfigs
                  • configureSampler
                    • configureWithConfigElements
                      • addDirectParentControllers
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
                      http://www.vxiaotou.com