JMeter5.1核心类TestCompiler源码分析

2021-12-11 15:53:17 浏览数 (1)

概述

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);
        }
    }

0 人点赞