Jmeter源碼框架

首先jmeter框架入口類: NewDriver類(src/core/org/apache/jmeter/NewDriver.java)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類:apache

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

能夠看出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:app

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

進入configuresceneless

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類post

刪掉冗餘,只留核心:測試

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]

相關文章
相關標籤/搜索