首先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]