【转自:https://blog.csdn.net/zuozewei/article/details/85042829】
源码下载地址:
https://github.com/apache/jmeter
废话不多说,下面进入正题~
一、源码结构
1. 工程目录
2. 源码目录
3. 源码分析
运行机制
- HashTree 是 JMeter 执行测试依赖的数据结构,在执行测试之前进行配置测试数据,HashTree将数据组织到一个递归树结构中,并提供了操作该结构的方法
- StandardJMeterEngine 执行JMeter 测试 ,直接用于本地 GUI 和非 GUI 调用,或者在服务器模式下运行时由 RemoteJMeterEngineImpl 启动
- JMeterEngine 接口被运行 JMeter的测试类实现,此接口共8个方法,JMeterEngine本质就是一个线程。
二、代码分析
此处以非GUI模式运行JMeter为例,了解下JMeter的运行机制。首先我们找到入口类 NewDriver。
/** * The main program which actually runs JMeter. * mian方法 * @param args * the command line arguments */ 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.getDeclaredConstructor().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 类的 start 方法。
接下来我们看下 start 方法
/** * Takes the command line arguments and uses them to determine how to * startup JMeter. * 根据命令行执行不同的操作 * 主要功能为:1)startgui 2)startnogui * * Called reflectively by {@link NewDriver#main(String[])} * @param args The arguments for JMeter */ public void start(String[] args) { // 解析命令号参数的类 CLArgsParser parser = new CLArgsParser(args, options); // 错误信息 String error = parser.getErrorString(); if (error == null){// Check option combinations 检查选项组合 boolean gui = parser.getArgumentById(NONGUI_OPT)==null; boolean nonGuiOnly = parser.getArgumentById(REMOTE_OPT)!=null || parser.getArgumentById(REMOTE_OPT_PARAM)!=null || parser.getArgumentById(REMOTE_STOP)!=null; if (gui && nonGuiOnly) { error = "-r and -R and -X are only valid in non-GUI mode"; } } // 输出错误信息 if (null != error) { System.err.println("Error: " + error);//NOSONAR System.out.println("Usage");//NOSONAR System.out.println(CLUtil.describeOptions(options).toString());//NOSONAR // repeat the error so no need to scroll back past the usage to see it System.out.println("Error: " + error);//NOSONAR return; } try { // 初始化配置,同时初始化JMeter日志 initializeProperties(parser); // Also initialises JMeter logging Thread.setDefaultUncaughtExceptionHandler( (Thread t, Throwable e) -> { if (!(e instanceof ThreadDeath)) { log.error("Uncaught exception: ", e); System.err.println("Uncaught Exception " + e + ". See log file for details.");//NOSONAR } }); if (log.isInfoEnabled()) { log.info(JMeterUtils.getJMeterCopyright()); log.info("Version {}", JMeterUtils.getJMeterVersion()); log.info("java.version={}", System.getProperty("java.version"));//$NON-NLS-1$ //$NON-NLS-2$ log.info("java.vm.name={}", System.getProperty("java.vm.name"));//$NON-NLS-1$ //$NON-NLS-2$ log.info("os.name={}", System.getProperty("os.name"));//$NON-NLS-1$ //$NON-NLS-2$ log.info("os.arch={}", System.getProperty("os.arch"));//$NON-NLS-1$ //$NON-NLS-2$ log.info("os.version={}", System.getProperty("os.version"));//$NON-NLS-1$ //$NON-NLS-2$ log.info("file.encoding={}", System.getProperty("file.encoding"));//$NON-NLS-1$ //$NON-NLS-2$ log.info("Max memory ={}", Runtime.getRuntime().maxMemory()); log.info("Available Processors ={}", Runtime.getRuntime().availableProcessors()); log.info("Default Locale={}", Locale.getDefault().getDisplayName()); log.info("JMeter Locale={}", JMeterUtils.getLocale().getDisplayName()); log.info("JMeterHome={}", JMeterUtils.getJMeterHome()); log.info("user.dir ={}", System.getProperty("user.dir"));//$NON-NLS-1$ //$NON-NLS-2$ log.info("PWD ={}", new File(".").getCanonicalPath());//$NON-NLS-1$ log.info("IP: {} Name: {} FullName: {}", JMeterUtils.getLocalHostIP(), JMeterUtils.getLocalHostName(), JMeterUtils.getLocalHostFullName()); } setProxy(parser); 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 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$ // 判断 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"));//NOSONAR $NON-NLS-1$ } else if (parser.getArgumentById(OPTIONS_OPT) != null) { displayAsciiArt(); System.out.println(CLUtil.describeOptions(options).toString());//NOSONAR } else if (parser.getArgumentById(SERVER_OPT) != null) { // Start the server 启动服务 try { RemoteJMeterEngineImpl.startServer(RmiUtils.getRmiRegistryPort()); // $NON-NLS-1$ startOptionalServers(); } catch (Exception ex) { System.err.println("Server failed to start: "+ex);//NOSONAR log.error("Giving up, as server failed with:", ex); throw ex; } } else { String testFile=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 testReportOpt = parser.getArgumentById(REPORT_GENERATING_OPT); if (testReportOpt != null) { // generate report from existing file 从现有文件生成报告 String reportFile = testReportOpt.getArgument(); extractAndSetReportOutputFolder(parser, false); ReportGenerator generator = new ReportGenerator(reportFile, null); generator.generate(); } else if (parser.getArgumentById(NONGUI_OPT) == null) { // not non-GUI => GUI // 在GUI模式下执行 startGui(testFile); startOptionalServers(); } else { // NON-GUI must be true 必须为无GUI模式 extractAndSetReportOutputFolder(parser, deleteResultFile); 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 && jtlFile == null) { throw new IllegalUserActionException( "Option -"+ ((char)REPORT_AT_END_OPT)+" requires -"+((char)LOGFILE_OPT )+ " option"); } // 无GUI执行 startNonGui(testFile, jtlFile, rem, reportAtEndOpt != null); startOptionalServers(); } } } catch (IllegalUserActionException e) {// NOSONAR System.out.println("Incorrect Usage:"+e.getMessage());//NOSONAR System.out.println(CLUtil.describeOptions(options).toString());//NOSONAR } catch (Throwable e) { // NOSONAR log.error("An error occurred: ", e); System.out.println("An error occurred: " + e.getMessage());//NOSONAR // FIXME Should we exit here ? If we are called by Maven or Jenkins System.exit(1); } }
start 方法主要还是根据命令执行不同的启动方法
无 GUI 方法启动
private void startNonGui(String testFile, 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 System.setProperty(JMETER_NON_GUI, "true");// $NON-NLS-1$ JMeter driver = new JMeter();// TODO - why does it create a new instance? driver.remoteProps = this.remoteProps; driver.remoteStop = this.remoteStop; driver.deleteResultFile = this.deleteResultFile; PluginManager.install(this, false); String remoteHostsString = null; if (remoteStart != null) { remoteHostsString = remoteStart.getArgument(); if (remoteHostsString == null) { remoteHostsString = JMeterUtils.getPropDefault( "remote_hosts", //$NON-NLS-1$ "127.0.0.1");//NOSONAR $NON-NLS-1$ } } if (testFile == null) { throw new IllegalUserActionException("Non-GUI runs require a test plan"); } // 运行场景 driver.runNonGui(testFile, logFile, remoteStart != null, remoteHostsString, generateReportDashboard); }
GUI方法启动
/** * Starts up JMeter in GUI mode * JMeter GUI启动 */ private void startGui(String testFile) { System.out.println("================================================================================");//NOSONAR System.out.println("Don‘t use GUI mode for load testing !, only for Test creation and Test debugging.");//NOSONAR System.out.println("For load testing, use NON GUI Mode:");//NOSONAR System.out.println(" jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]");//NOSONAR System.out.println("& increase Java Heap to meet your test requirements:");//NOSONAR System.out.println(" Modify current env variable HEAP=\"-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m\" in the jmeter batch file");//NOSONAR System.out.println("Check : https://jmeter.apache.org/usermanual/best-practices.html");//NOSONAR System.out.println("================================================================================");//NOSONAR // 设置加载进度条 splash.setProgress(10/30/60/90/100) SplashScreen splash = new SplashScreen(); splash.showScreen(); String jMeterLaf = LookAndFeelCommand.getJMeterLaf(); try { log.info("Setting LAF to: {}", jMeterLaf); UIManager.setLookAndFeel(jMeterLaf); } catch (Exception ex) { log.warn("Could not set LAF to: {}", jMeterLaf, ex); } splash.setProgress(10); JMeterUtils.applyHiDPIOnFonts(); PluginManager.install(this, true); JMeterTreeModel treeModel = new JMeterTreeModel(); splash.setProgress(30); JMeterTreeListener treeLis = new JMeterTreeListener(treeModel); final ActionRouter instance = ActionRouter.getInstance(); instance.populateCommandMap(); //这个方法会去寻找<project>/lib/ext 下所有的jar splash.setProgress(60); treeLis.setActionHandler(instance); GuiPackage.initInstance(treeLis, treeModel); splash.setProgress(80); MainFrame main = new MainFrame(treeModel, treeLis); splash.setProgress(100); ComponentUtil.centerComponentInWindow(main, 80); main.setLocationRelativeTo(splash); main.setVisible(true); main.toFront(); 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); } splash.setProgress(100); splash.close(); }
在来看下 runNonGui 方法,主要功能是执行脚本。
// run test in batch mode 批处理运行测试 private void runNonGui(String testFile, String logFile, boolean remoteStart, String remoteHostsString, boolean generateReportDashboard) { try { // 获取脚本文件 File f = new File(testFile); if (!f.exists() || !f.isFile()) { println("Could not open " + testFile); return; } // 设置脚本文件 FileServer.getFileServer().setBaseForScript(f); HashTree tree = SaveService.loadTree(f); @SuppressWarnings("deprecation") // Deliberate use of deprecated ctor JMeterTreeModel treeModel = new JMeterTreeModel(new Object());// NOSONAR 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); } // Ensure tree is interpreted (ReplaceableControllers are replaced) // For GUI runs this is done in Start.java // 将测试文件(.jmx文件)解析成HashTree HashTree clonedTree = convertSubTree(tree, true); Summariser summariser = null; String summariserName = JMeterUtils.getPropDefault("summariser.name", "");//$NON-NLS-1$ if (summariserName.length() > 0) { log.info("Creating summariser <{}>", summariserName); println("Creating summariser <" + summariserName + ">"); summariser = new Summariser(summariserName); } ResultCollector resultCollector = null; if (logFile != null) { resultCollector = new ResultCollector(summariser); resultCollector.setFilename(logFile); clonedTree.add(clonedTree.getArray()[0], resultCollector); } else { // only add Summariser if it can not be shared with the ResultCollector if (summariser != null) { clonedTree.add(clonedTree.getArray()[0], summariser); } } if (deleteResultFile) { SearchByClass<ResultCollector> resultListeners = new SearchByClass<>(ResultCollector.class); clonedTree.traverse(resultListeners); Iterator<ResultCollector> irc = resultListeners.getSearchResults().iterator(); while (irc.hasNext()) { ResultCollector rc = irc.next(); File resultFile = new File(rc.getFilename()); if (resultFile.exists() && !resultFile.delete()) { throw new IllegalStateException("Could not delete results file " + resultFile.getAbsolutePath() + "(canRead:"+resultFile.canRead()+", canWrite:"+resultFile.canWrite()+")"); } } } ReportGenerator reportGenerator = null; if (logFile != null && generateReportDashboard) { reportGenerator = new ReportGenerator(logFile, resultCollector); } // 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 clonedTree.add(clonedTree.getArray()[0], new RemoteThreadsListenerTestElement()); List<JMeterEngine> engines = new LinkedList<>(); clonedTree.add(clonedTree.getArray()[0], new ListenToTest(remoteStart && remoteStop ? engines : null, reportGenerator)); println("Created the tree successfully using "+testFile); if (!remoteStart) { // 实例化一个JMeterEngine来对付脚本,JMeterEngine本质就是一个线程 JMeterEngine engine = new StandardJMeterEngine(); engine.configure(clonedTree); long now=System.currentTimeMillis(); println("Starting the test @ "+new Date(now)+" ("+now+")"); // 调用runTest方法 engine.runTest(); engines.add(engine); } else { java.util.StringTokenizer st = new java.util.StringTokenizer(remoteHostsString, ",");//$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); // NOSONAR distributedRunner.setStdErr(System.err); // NOSONAR distributedRunner.init(hosts, clonedTree); engines.addAll(distributedRunner.getEngines()); distributedRunner.start(); } startUdpDdaemon(engines); } catch (Exception e) { System.out.println("Error in NonGUIDriver " + e.toString());//NOSONAR log.error("Error in NonGUIDriver", e); } }
整体再来梳理下JMeter类逻辑,抛开 GUI 和 Remote test相关的代码,简单说,JMeter 做的事情主要有:
- 解析命令行参数,加载配置文件;
- 将 .Jmx 文件解析成 HashTree;
- 实例化一个StandardJMeterEngine,并把测试的工作交给JMeterEngine;
当然,JMeter类还有其他重要的职责,比如监听所有的 JMeterEngine ,当接收到 GUI 的 StopTestNow / Shutdown 等命令时候来调用JMeterEngine接口相应的方法。
接下来看下JMeterEngine,看下这个接口都提供哪些方法?
/** * This interface is implemented by classes that can run JMeter tests. */ public interface JMeterEngine { /** * Configure engine * 配置引擎 * @param testPlan the test plan */ void configure(HashTree testPlan); /** * Runs the test * 执行测试 * @throws JMeterEngineException if an error occurs */ void runTest() throws JMeterEngineException; /** * Stop test immediately interrupting current samplers * 停止测试,立即打断当前samplers */ default void stopTest() { stopTest(true); } /** * 停止测试,根据参数是否立即打断当前samplers * @param now boolean that tell wether stop is immediate (interrupt) or not (wait for current sample end) */ void stopTest(boolean now); /** * Stop test if running * 停止测试运行 */ void reset(); /** * set Properties on engine * 设置引擎属性 * @param p the properties to set */ void setProperties(Properties p); /** * Exit engine * 退出引擎 */ void exit(); /** * 引擎是否活跃 * @return boolean Flag to show whether engine is active (true when test is running). Set to false at end of test */ boolean isActive(); }
JMeterEngine 依赖于 HashTree,而 HashTree 是由 jmx 文件解析而来,每一个 JMeter 测试计划都会对应一个 jmx 文件。所以我们只要生成合理的 jmx 文件,就可以通过 JMeterEngine 压测引擎去执行测试任务。
具体 jmx 文件的生成方式,我们可以借鉴JMeter GUI模式下 jmx 文件生成方式。在这里我们的演示的处理方式是,先定义每个组件的生成方式,然后再按一定结构组装各个组件,示意代码如下。
三、JAVA运行JMeter示例
遵循以下规则:
- 将JMeter文件安装在某个地方
- 在项目lib或者JMeter安装的/ lib/ext文件夹中获取所需的 JMeter jar包。
JMeter的“压测引擎”就是 StandardJMeterEngine ,我们需要扩展此类或实现自己的JMeterEngine接口。
示例生成并读取.jmx文件并执行它,代码如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zuozewei</groupId> <artifactId>jmeter-from-code</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.zuozewei.demo.JMeterFromScratch</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_java</artifactId> <version>4.0</version> </dependency> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_http</artifactId> <version>4.0</version> </dependency> </dependencies> </project>
测试类
public class JMeterFromScratch { public static void main(String[] argv) throws Exception { // 设置jmeterHome路径 String jmeterHome1 = "/Users/apple/Downloads/performance/apache-jmeter-4.0"; //File jmeterHome = new File(System.getProperty("jmeter.home")); File jmeterHome = new File(jmeterHome1); String slash = System.getProperty("file.separator"); if (jmeterHome.exists()) { File jmeterProperties = new File(jmeterHome.getPath() + slash + "bin" + slash + "jmeter.properties"); if (jmeterProperties.exists()) { //JMeter Engine 引擎 StandardJMeterEngine jmeter = new StandardJMeterEngine(); //JMeter initialization (properties, log levels, locale, etc) JMeterUtils.setJMeterHome(jmeterHome.getPath()); JMeterUtils.loadJMeterProperties(jmeterProperties.getPath()); JMeterUtils.initLogging();// you can comment this line out to see extra log messages of i.e. DEBUG level JMeterUtils.initLocale(); // JMeter Test Plan, basically JOrphan HashTree HashTree testPlanTree = new HashTree(); // 第一个 HTTP Sampler - 打开 baidu.com HTTPSamplerProxy examplecomSampler = new HTTPSamplerProxy(); examplecomSampler.setDomain("baidu.com"); examplecomSampler.setPort(80); examplecomSampler.setPath("/"); examplecomSampler.setMethod("GET"); examplecomSampler.setName("Open baidu.com"); examplecomSampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName()); examplecomSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName()); // 第二个 HTTP Sampler - 打开 qq.com HTTPSamplerProxy blazemetercomSampler = new HTTPSamplerProxy(); blazemetercomSampler.setDomain("qq.com"); blazemetercomSampler.setPort(80); blazemetercomSampler.setPath("/"); blazemetercomSampler.setMethod("GET"); blazemetercomSampler.setName("Open qq.com"); blazemetercomSampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName()); blazemetercomSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName()); // Loop Controller 循环控制 LoopController loopController = new LoopController(); loopController.setLoops(1); loopController.setFirst(true); loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName()); loopController.setProperty(TestElement.GUI_CLASS, LoopControlPanel.class.getName()); loopController.initialize(); // Thread Group 线程组 ThreadGroup threadGroup = new ThreadGroup(); threadGroup.setName("Example Thread Group"); threadGroup.setNumThreads(1); threadGroup.setRampUp(1); threadGroup.setSamplerController(loopController); threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName()); threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName()); // Test Plan 测试计划 TestPlan testPlan = new TestPlan("Create JMeter Script From Java Code"); testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName()); testPlan.setProperty(TestElement.GUI_CLASS, TestPlanGui.class.getName()); testPlan.setUserDefinedVariables((Arguments) new ArgumentsPanel().createTestElement()); // Construct Test Plan from previously initialized elements // 从以上初始化的元素构造测试计划 testPlanTree.add(testPlan); HashTree threadGroupHashTree = testPlanTree.add(testPlan, threadGroup); threadGroupHashTree.add(blazemetercomSampler); threadGroupHashTree.add(examplecomSampler); // save generated test plan to JMeter‘s .jmx file format // 将生成的测试计划保存为JMeter的.jmx文件格式 SaveService.saveTree(testPlanTree, new FileOutputStream(jmeterHome + slash + "example.jmx")); //add Summarizer output to get test progress in stdout like: // 在stdout中添加summary输出,得到测试进度,如: // summary = 2 in 1.3s = 1.5/s Avg: 631 Min: 290 Max: 973 Err: 0 (0.00%) Summariser summer = null; String summariserName = JMeterUtils.getPropDefault("summariser.name", "summary"); if (summariserName.length() > 0) { summer = new Summariser(summariserName); } // Store execution results into a .jtl file // 将执行结果存储到.jtl文件中 String logFile = jmeterHome + slash + "example.jtl"; ResultCollector logger = new ResultCollector(summer); logger.setFilename(logFile); testPlanTree.add(testPlanTree.getArray()[0], logger); // Run Test Plan // 执行测试计划 jmeter.configure(testPlanTree); jmeter.run(); System.out.println("Test completed. See " + jmeterHome + slash + "example.jtl file for results"); System.out.println("JMeter .jmx script is available at " + jmeterHome + slash + "example.jmx"); System.exit(0); } } System.err.println("jmeter.home property is not set or pointing to incorrect location"); System.exit(1); } }
测试结果
summary = 2 in 00:00:03 = 0.8/s Avg: 951 Min: 933 Max: 969 Err: 0 (0.00%) Test completed. See /Users/apple/Downloads/performance/apache-jmeter-4.0/example.jtl file for results JMeter .jmx script is available at /Users/apple/Downloads/performance/apache-jmeter-4.0/example.jmx Process finished with exit code 0
本文源码地址:
https://github.com/zuozewei/JMeter-Examples
---------------------
作者:zuozewei
来源:CSDN
原文:https://blog.csdn.net/zuozewei/article/details/85042829
版权声明:本文为博主原创文章,转载请附上博文链接!
原文地址:https://www.cnblogs.com/yigui/p/11072935.html