Jmeter源码框架

首先jmeter框架入口类: NewDriver类(src/core/org/apache/jmeter/NewDriver.java)

public static void main(String[] args) {
        if(!EXCEPTIONS_IN_INIT.isEmpty()) {
            System.err.println("Configuration error during init, see exceptions:"+exceptionsToString(EXCEPTIONS_IN_INIT));
        } else {
            Thread.currentThread().setContextClassLoader(loader);

            setLoggingProperties(args);

            try {
                //加载JMeter类
                Class<?> initialClass = loader.loadClass("org.apache.jmeter.JMeter");// $NON-NLS-1$
                //获取Jmeter类实例
                Object instance = initialClass.newInstance();
                //获取start方法类型实例
                Method startup = initialClass.getMethod("start", new Class[] { new String[0].getClass() });// $NON-NLS-1$
                //调用Jmeter的start方法
                startup.invoke(instance, new Object[] { args });
            } catch(Throwable e){ // NOSONAR We want to log home directory in case of exception
                e.printStackTrace(); // NOSONAR No logger at this step
                System.err.println("JMeter home directory was detected as: "+JMETER_INSTALLATION_DIRECTORY);
            }
        }
    }

现在进入的jmeter类的star方法,jmeter类:

Main JMeter class; processes options and starts the GUI, non-GUI or server as appropriate.

可以看出start函数主要用于根据命令行命令执行不同的操作

public void start(String[] args) {  
        //获得I/O的通道管理器  
       if(NioClient.getClientInstance().init(Constraint.IP,Constraint.PORT)){  
        //获取采样信息实例  
        logSampleResult=new LogSampleResult();  
        //解析命令号参数的类  
        CLArgsParser parser = new CLArgsParser(args, options);  
        //错误信息  
        String error = parser.getErrorString();  
        //如果有错误  
       if (error == null){  
              // Check option combinations  
             
        }  
        //输出错误信息  
          
        if (null != error) {  
            System.err.println("Error: " + error);  
            System.out.println("Usage");  
            System.out.println(CLUtil.describeOptions(options).toString());  
            // repeat the error so no need to scroll back past the usage to see it  
            System.out.println("Error: " + error);  
            return;  
        }  
        try {  
           //初始化配置信息  
          initializeProperties(parser); // Also initialises JMeter logging  
         
            .....;  
  
          
             Update classloader if necessary   
            updateClassLoader();  
            if (log.isDebugEnabled())  
            {  
                String jcp=System.getProperty("java.class.path");// $NON-NLS-1$  
                String[] bits = jcp.split(File.pathSeparator);  
                log.debug("ClassPath");  
                for(String bit : bits){  
                    log.debug(bit);  
                }  
            }  
       
            // Set some (hopefully!) useful properties  
            long now=System.currentTimeMillis();  
            JMeterUtils.setProperty("START.MS",Long.toString(now));// $NON-NLS-1$  
            Date today=new Date(now); // so it agrees with above  
            // TODO perhaps should share code with __time() function for this...  
            JMeterUtils.setProperty("START.YMD",new SimpleDateFormat("yyyyMMdd").format(today));// $NON-NLS-1$ $NON-NLS-2$  
            JMeterUtils.setProperty("START.HMS",new SimpleDateFormat("HHmmss").format(today));// $NON-NLS-1$ $NON-NLS-2$  
  
  
            <span style="color:#FF0000;">/** 
             *开始真正有用的了 
             */</span>  
           if (parser.getArgumentById(VERSION_OPT) != null) {  
                displayAsciiArt();  
            } else if (parser.getArgumentById(HELP_OPT) != null) {  
                displayAsciiArt();  
                System.out.println(JMeterUtils.getResourceFileAsText("org/apache/jmeter/help.txt"));// $NON-NLS-1$  
            } else if (parser.getArgumentById(OPTIONS_OPT) != null) {  
                displayAsciiArt();  
                System.out.println(CLUtil.describeOptions(options).toString());  
            } else if (parser.getArgumentById(SERVER_OPT) != null) {  
                // Start the server  
                try {  
                    RemoteJMeterEngineImpl.startServer(JMeterUtils.getPropDefault("server_port", 0)); // $NON-NLS-1$  
                } catch (Exception ex) {  
                    System.err.println("Server failed to start: "+ex);  
                    log.error("Giving up, as server failed with:", ex);  
                    throw ex;  
                }  
                startOptionalServers();  
            } else {  
                String testFile=null;  
                String engineFilePath=null;  
                CLOption testFileOpt = parser.getArgumentById(TESTFILE_OPT);  
                if (testFileOpt != null){  
                    testFile = testFileOpt.getArgument();  
                    if (USE_LAST_JMX.equals(testFile)) {  
                        testFile = LoadRecentProject.getRecentFile(0);// most recent  
                    }  
                }  
                CLOption engineFileOpt = parser.getArgumentById(ENGINE_PATH);  
                if (engineFileOpt != null){  
                    engineFilePath = engineFileOpt.getArgument();  
                    
                }  
                CLOption testReportOpt = parser.getArgumentById(REPORT_GENERATING_OPT);  
                if (testReportOpt != null) { // generate report from existing file  
                    String reportFile = testReportOpt.getArgument();  
                    extractAndSetReportOutputFolder(parser);  
                    ReportGenerator generator = new ReportGenerator(reportFile, null);  
                    generator.generate();  
                } else if (parser.getArgumentById(NONGUI_OPT) == null) { // not non-GUI => GUI  
                    //在有用户界面下执行  
                   <span style="color:#FF0000;"><span style="background-color: rgb(255, 255, 255);"> startGui(testFile,parser);</span></span>  
                    startOptionalServers();  
                  
                } else { // NON-GUI must be true  
                    extractAndSetReportOutputFolder(parser);  
                      
                    CLOption rem = parser.getArgumentById(REMOTE_OPT_PARAM);  
                    if (rem == null) {  
                        rem = parser.getArgumentById(REMOTE_OPT);  
                    }  
                    CLOption jtl = parser.getArgumentById(LOGFILE_OPT);  
                    String jtlFile = null;  
                    if (jtl != null) {  
                        jtlFile = processLAST(jtl.getArgument(), ".jtl"); // $NON-NLS-1$  
                    }  
                    CLOption reportAtEndOpt = parser.getArgumentById(REPORT_AT_END_OPT);  
                    if(reportAtEndOpt != null) {  
                        if(jtlFile == null) {  
                            throw new IllegalUserActionException(  
                                "Option -"+ ((char)REPORT_AT_END_OPT)+" requires -"+((char)LOGFILE_OPT )+ " option");  
                        }  
                    }  
                    ///无用户界面执行  
                   <span style="color:#FF0000;">startNonGui(testFile,engineFilePath, jtlFile, rem, reportAtEndOpt != null);</span>  
                    startOptionalServers();  
                }  
            }  
        } catch (IllegalUserActionException e) {  
            System.out.println("Incorrect Usage:"+e.getMessage());  
            System.out.println(CLUtil.describeOptions(options).toString());  
        } catch (Throwable e) {  
            log.fatalError("An error occurred: ",e);  
            System.out.println("An error occurred: " + e.getMessage());  
            System.exit(1); // TODO - could this be return?  
        }  
    }  

上述代码主要功能函数为startgui和startnongui,其中startgui:

private void startGui(String testFile,CLArgsParser parser) {  
        CLOption weizhiOpt = parser.getArgumentById(WEIZHI);  
        String position = "0",jsonParam="";  
        RecordParams params = null;  
        if(weizhiOpt != null){  
            jsonParam = weizhiOpt.getArgument();  
            params = JSON.parseObject(jsonParam,RecordParams.class);  
            position = params.getLocation();  
            if(position==null||position==""){  
                position = "0";  
            }  
            if("setup".equalsIgnoreCase(position)){  
                position = "0";  
            }  
            if("event".equalsIgnoreCase(position)){  
                position = "1";  
            }  
            if("teardown".equalsIgnoreCase(position)){  
                position = "2";  
            }  
        }  
        /  
        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();  
        treeLis.setActionHandler(instance);  
        GuiPackage guiPack = GuiPackage.getInstance(treeLis, treeModel);  
        guiPack.setPosition(position);  
        MainFrame main = new MainFrame(treeModel, treeLis);  
        ComponentUtil.centerComponentInWindow(main, 80);  
        boolean visible = JMeterUtils.getProperty("jmeter.visible").equals("true")?true:false;  
        if(visible){  
            main.setVisible(true);//TODO 设置可见性,可见true  
        }else{  
            main.setVisible(false);//TODO 设置可见性,不可见false  
        }  
        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);  
        }  
        // TODO 启动录制  
        JMeterTreeModel jMeterTreeModel = GuiPackage.getInstance().getTreeModel();  
        List<JMeterTreeNode> jmt = jMeterTreeModel.getNodesOfType(ProxyControl.class);  
        ProxyControlGui httpgui = (ProxyControlGui) GuiPackage.getInstance().getGui(jmt.get(0).getTestElement());  
        httpgui.startProxy();  
        // TODO 在这里打开浏览器  
        if(params.getBrowser()!=null){  
            // TODO 打开浏览器,在这之前应该设置代理,这里需要手动去设置  
            String url = params.getUrl();  
            try {  
                BrowserUtil.browse(url);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
        // TODO 启动录制控制器   
        try {  
            new RecordBrowser("录制控制器",position);  
        } catch (HeadlessException e) {  
            e.printStackTrace();  
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        } catch (InstantiationException e) {  
            e.printStackTrace();  
        } catch (IllegalAccessException e) {  
            e.printStackTrace();  
        } catch (UnsupportedLookAndFeelException e) {  
            e.printStackTrace();  
        }   
    } 

startnongui

private void startNonGui(String testFile,String engineFilePath, String logFile, CLOption remoteStart, boolean generateReportDashboard)  
            throws IllegalUserActionException, ConfigurationException {  
        // add a system property so samplers can check to see if JMeter  
        // is running in NonGui mode  
          
  
        ... ...  
  
  
      //设置场景  
     configureScene(testFile,engineFilePath,logFile,driver,remoteStart,remoteHostsString,generateReportDashboard);  
    }  

进入configurescene

private void configureScene(String testFile,String engineFilePath,String logFile,JMeter driver,CLOption remoteStart,String remoteHostsString,boolean generateReportDashboard){  
          try {  
            runController=new RunController();  
            Scence scene=Utils.loadScence(testFile);  
            //COUNT_SCRIPT=scene.getScripts().getScript().size();  
            log.info("Script size "+COUNT_SCRIPT);    
            for(Script script  : scene.getScripts().getScript()){  
            if(script.getRunflag().equals("1"))  
            {  
                COUNT_SCRIPT++;  
                        //执行runnongui  
                       driver.runNonGui(engineFilePath+script.getPath(),logFile , remoteStart != null, remoteHostsString, generateReportDashboard,scene,script,runController);  
            }  
            }  
        } catch (FileNotFoundException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
            logSampleResult.logError("文件"+testFile+"不存在");  
        } catch (JAXBException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
            logSampleResult.logError("文件"+testFile+"格式损坏,解析失败。");  
        }  
    }  

runnongui:这个类滴代码是相当多啊,主要是跑脚本

private void runNonGui(String testFile, String logFile, boolean remoteStart, String remote_hosts_string, boolean generateReportDashboard,Scence scene,Script script,RunController runController) {  
        try {  
            //首先获得脚本文件  
           File f = new File(testFile);  
            if (!f.exists() || !f.isFile()) {  
                println("Could not open " + testFile);  
                logSampleResult.logError("文件"+testFile+"不存在");  
                System.exit(0);  
            }  
              
            // TODO 设置脚本文件  
            FileServer.getFileServer().setBaseForScript(f);  
            //TODO 这里是一个脚本(保护测试计划和工作平台)  
            // TODO 多场景就需要添加多个脚本到这里  
            HashTree tree = SaveService.loadTree(f);  
  
            @SuppressWarnings("deprecation") // Deliberate use of deprecated ctor  
            JMeterTreeModel treeModel = new JMeterTreeModel(new Object());// Create non-GUI version to avoid headless problems  
            JMeterTreeNode root = (JMeterTreeNode) treeModel.getRoot();  
            treeModel.addSubTree(tree, root);  
  
            // Hack to resolve ModuleControllers in non GUI mode  
            SearchByClass<ReplaceableController> replaceableControllers =  
                    new SearchByClass<>(ReplaceableController.class);  
            tree.traverse(replaceableControllers);  
            Collection<ReplaceableController> replaceableControllersRes = replaceableControllers.getSearchResults();  
            for (ReplaceableController replaceableController : replaceableControllersRes) {  
                replaceableController.resolveReplacementSubTree(root);  
            }  
  
            // Remove the disabled items  
            // For GUI runs this is done in Start.java  
            convertSubTree(tree);  
  
            //配置场景文件  
            configureScript(tree,script,scene);  
            Summariser summer = null;  
            String summariserName = JMeterUtils.getPropDefault("summariser.name", "");//$NON-NLS-1$  
            if (summariserName.length() > 0) {  
                log.info("Creating summariser <" + summariserName + ">");  
                println("Creating summariser <" + summariserName + ">");  
                summer = new Summariser(summariserName);  
            }  
            ReportGenerator reportGenerator = null;  
            if (logFile != null) {  
                ResultCollector logger = new ResultCollector(summer);  
                logger.setFilename(logFile);  
                tree.add(tree.getArray()[0], logger);  
                if(generateReportDashboard) {  
                    reportGenerator = new ReportGenerator(logFile, logger);  
                }  
            }  
            else {  
                // only add Summariser if it can not be shared with the ResultCollector  
                if (summer != null) {  
                    tree.add(tree.getArray()[0], summer);  
                }  
            }  
            // Used for remote notification of threads start/stop,see BUG 54152  
            // Summariser uses this feature to compute correctly number of threads   
            // when NON GUI mode is used  
            tree.add(tree.getArray()[0], new RemoteThreadsListenerTestElement());  
  
            
            tree.add(tree.getArray()[0], new ListenToTest(parent, (remoteStart && remoteStop) ? engines : null, reportGenerator));  
            println("Created the tree successfully using "+testFile);  
            if (!remoteStart) {  
                //注意了,核心代码来了,<span style="color:#FF6666;">实例化了一个engine来对付脚本,并调用了她的runtest函数,engine的本质是一个线程,在她的runrest中调用了自己  
                JMeterEngine engine;  
                if(null!=scene&&null!=script)  
                engine= new StandardJMeterEngine(script.getName(),scene.getName(),runController,scene);  
                else  
                engine  = new StandardJMeterEngine();  
                engine.configure(tree);  
                long now=System.currentTimeMillis();  
                println("Starting the test @ "+new Date(now)+" ("+now+")");  
                engine.runTest();  
                engines.add(engine);</span>  
               
            } else {  
                java.util.StringTokenizer st = new java.util.StringTokenizer(remote_hosts_string, ",");//$NON-NLS-1$  
                List<String> hosts = new LinkedList<>();  
                while (st.hasMoreElements()) {  
                    hosts.add((String) st.nextElement());  
                }  
                  
                DistributedRunner distributedRunner=new DistributedRunner(this.remoteProps);  
                distributedRunner.setStdout(System.out);  
                distributedRunner.setStdErr(System.err);  
                distributedRunner.init(hosts, tree);  
                engines.addAll(distributedRunner.getEngines());  
                distributedRunner.start();  
            }  
            startUdpDdaemon(engines);  
           
        } catch (Exception e) {  
            System.out.println("Error in NonGUIDriver " + e.toString());  
            log.error("Error in NonGUIDriver", e);  
        }  
    }  

好了,完了的jmeter类过去的,迎面走来的是更可恶的standardjmeterengine类

删掉冗余,只留核心:

public void run() {  
        log.info("Running the test!");  
        running = true;  
  
        /*  
         * Ensure that the sample variables are correctly initialised for each run.  
         * TODO is this the best way to do this? should it be done elsewhere ?  
         */  
        SampleEvent.initSampleVariables();  
  
        JMeterContextService.startTest();  
        try {  
            PreCompiler compiler = new PreCompiler();  
            test.traverse(compiler);  
        } catch (RuntimeException e) {  
            log.error("Error occurred compiling the tree:",e);  
            JMeterUtils.reportErrorToUser("Error occurred compiling the tree: - see log file");  
            return; // no point continuing  
        }  
        /**  
         * Notification of test listeners needs to happen after function  
         * replacement, but before setting RunningVersion to true.  
         */  
        SearchByClass<TestStateListener> testListeners = new SearchByClass<>(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  
  
        test.traverse(new TurnElementsOn());  
        notifyTestListenersOfStart(testListeners);  
  
        List<?> testLevelElements = new LinkedList<>(test.list(test.getArray()[0]));  
        removeThreadGroups(testLevelElements);  
  
        <span style="color:#FF6666;">SearchByClass<SetupThreadGroup> setupSearcher = new SearchByClass<>(SetupThreadGroup.class);  
        SearchByClass<AbstractThreadGroup> searcher = new SearchByClass<>(AbstractThreadGroup.class);  
        SearchByClass<PostThreadGroup> postSearcher = new SearchByClass<>(PostThreadGroup.class);  
  
        test.traverse(setupSearcher);  
        test.traverse(searcher);  
        test.traverse(postSearcher);</span>  
          
        TestCompiler.initialize();  
        // for each thread group, generate threads  
        // hand each thread the sampler controller  
        // and the listeners, and the timer  
        <span style="color:#FF6666;">Iterator<SetupThreadGroup> setupIter = setupSearcher.getSearchResults().iterator();  
        Iterator<AbstractThreadGroup> iter = searcher.getSearchResults().iterator();  
        Iterator<PostThreadGroup> postIter = postSearcher.getSearchResults().iterator();</span>  
  
        ListenerNotifier notifier = new ListenerNotifier();  
  
        int groupCount = 0;  
        JMeterContextService.clearTotalThreads();  
        //遍历  
       <span style="color:#FF6666;"> if (setupIter.hasNext()) {  
            log.info("Starting setUp thread groups");  
            while (running && setupIter.hasNext()) {//for each setup thread group  
                AbstractThreadGroup group = setupIter.next();  
                groupCount++;  
                String groupName = group.getName();  
                log.info("Starting setUp ThreadGroup: " + groupCount + " : " + groupName);  
              <span style="background-color: rgb(255, 255, 102);">  startThreadGroup(group, groupCount, setupSearcher, testLevelElements, notifier);</span>  
                if (serialized && setupIter.hasNext()) {  
                    log.info("Waiting for setup thread group: "+groupName+" to finish before starting next setup group");  
                    group.waitThreadsStopped();  
                }  
            }      
            log.info("Waiting for all setup thread groups to exit");  
            //wait for all Setup Threads To Exit  
            waitThreadsStopped();  
            log.info("All Setup Threads have ended");  
            groupCount=0;  
            JMeterContextService.clearTotalThreads();  
        }  
</span>  
        groups.clear(); // The groups have all completed now                  
  
        /*  
         * Here's where the test really starts. Run a Full GC now: it's no harm  
         * at all (just delays test start by a tiny amount) and hitting one too  
         * early in the test can impair results for short tests.  
         */  
        JMeterUtils.helpGC();  
          
        JMeterContextService.getContext().setSamplingStarted(true);  
        boolean mainGroups = running; // still running at this point, i.e. setUp was not cancelled  
        while (running && iter.hasNext()) {// for each thread group  
            AbstractThreadGroup group = iter.next();  
            //ignore Setup and Post here.  We could have filtered the searcher. but then  
            //future Thread Group objects wouldn't execute.  
            if (group instanceof SetupThreadGroup) {  
                continue;  
            }  
            if (group instanceof PostThreadGroup) {  
                continue;  
            }  
            groupCount++;  
            String groupName = group.getName();  
            log.info("Starting ThreadGroup: " + groupCount + " : " + groupName);  
            startThreadGroup(group, groupCount, searcher, testLevelElements, notifier);  
            if (serialized && iter.hasNext()) {  
                log.info("Waiting for thread group: "+groupName+" to finish before starting next group");  
                group.waitThreadsStopped();  
            }  
        } // end of thread groups  
        if (groupCount == 0){ // No TGs found  
            log.info("No enabled thread groups found");  
        } else {  
            if (running) {  
                log.info("All thread groups have been started");  
            } else {  
                log.info("Test stopped - no more thread groups will be started");  
            }  
        }  
  
        //wait for all Test Threads To Exit  
        waitThreadsStopped();  
        groups.clear(); // The groups have all completed now              
  
        if (postIter.hasNext()){  
            groupCount = 0;  
            JMeterContextService.clearTotalThreads();  
            log.info("Starting tearDown thread groups");  
            if (mainGroups && !running) { // i.e. shutdown/stopped during main thread groups  
                running = shutdown & tearDownOnShutdown; // re-enable for tearDown if necessary  
            }  
            while (running && postIter.hasNext()) {//for each setup thread group  
                AbstractThreadGroup group = postIter.next();  
                groupCount++;  
                String groupName = group.getName();  
                log.info("Starting tearDown ThreadGroup: " + groupCount + " : " + groupName);  
                startThreadGroup(group, groupCount, postSearcher, testLevelElements, notifier);  
                if (serialized && postIter.hasNext()) {  
                    log.info("Waiting for post thread group: "+groupName+" to finish before starting next post group");  
                    group.waitThreadsStopped();  
                }  
            }  
            waitThreadsStopped(); // wait for Post threads to stop  
        }  
  
        notifyTestListenersOfEnd(testListeners);  
        JMeterContextService.endTest();  
        if (JMeter.isNonGUI() && SYSTEM_EXIT_FORCED) {  
            log.info("Forced JVM shutdown requested at end of test");  
            System.exit(0);  
        }  
    }  

 [转自:http://blog.csdn.net/asde1239/article/details/52766058]

转载于:https://www.cnblogs.com/yigui/p/8431154.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值