赞
踩
流程图:
时序图:
功能目录结构
// src/launcher catalog
org.apache.jmeter.NewDriver
static { final List<URL> jars = new LinkedList<>(); final String initiaClasspath = System.getProperty(JAVA_CLASS_PATH); // Find JMeter home dir from the initial classpath String tmpDir; StringTokenizer tok = new StringTokenizer(initiaClasspath, File.pathSeparator); if (tok.countTokens() == 1 || (tok.countTokens() == 2 // Java on Mac OS can add a second entry to the initial classpath && OS_NAME_LC.startsWith("mac os x")// $NON-NLS-1$ ) ) { File jar = new File(tok.nextToken()); try { tmpDir = jar.getCanonicalFile().getParentFile().getParent(); } catch (IOException e) { tmpDir = null; } } else {// e.g. started from IDE with full classpath tmpDir = System.getProperty("jmeter.home","");// Allow override $NON-NLS-1$ $NON-NLS-2$ if (tmpDir.length() == 0) { File userDir = new File(System.getProperty("user.dir"));// $NON-NLS-1$ tmpDir = userDir.getAbsoluteFile().getParent(); } } tmpDir = tmpDir + File.separator + "apache-jmeter-5.3"; JMETER_INSTALLATION_DIRECTORY=tmpDir; /* * Does the system support UNC paths? If so, may need to fix them up * later */ boolean usesUNC = OS_NAME_LC.startsWith("windows");// $NON-NLS-1$ // Add standard jar locations to initial classpath StringBuilder classpath = new StringBuilder(); File[] libDirs = new File[] { new File(JMETER_INSTALLATION_DIRECTORY + File.separator + "lib"),// $NON-NLS-1$ $NON-NLS-2$ new File(JMETER_INSTALLATION_DIRECTORY + File.separator + "lib" + File.separator + "ext"),// $NON-NLS-1$ $NON-NLS-2$ new File(JMETER_INSTALLATION_DIRECTORY + File.separator + "lib" + File.separator + "junit")};// $NON-NLS-1$ $NON-NLS-2$ for (File libDir : libDirs) { File[] libJars = libDir.listFiles((dir, name) -> name.endsWith(".jar")); if (libJars == null) { new Throwable("Could not access " + libDir).printStackTrace(); // NOSONAR No logging here continue; } Arrays.sort(libJars); // Bug 50708 Ensure predictable order of jars for (File libJar : libJars) { try { String s = libJar.getPath(); // Fix path to allow the use of UNC URLs if (usesUNC) { if (s.startsWith("\\\\") && !s.startsWith("\\\\\\")) {// $NON-NLS-1$ $NON-NLS-2$ s = "\\\\" + s;// $NON-NLS-1$ } else if (s.startsWith("//") && !s.startsWith("///")) {// $NON-NLS-1$ $NON-NLS-2$ s = "//" + s;// $NON-NLS-1$ } } // usesUNC jars.add(new File(s).toURI().toURL());// See Java bug 4496398 classpath.append(CLASSPATH_SEPARATOR); classpath.append(s); } catch (MalformedURLException e) { // NOSONAR EXCEPTIONS_IN_INIT.add(new Exception("Error adding jar:"+libJar.getAbsolutePath(), e)); } } } // ClassFinder needs the classpath System.setProperty(JAVA_CLASS_PATH, initiaClasspath + classpath.toString()); loader = AccessController.doPrivileged( (PrivilegedAction<DynamicClassLoader>) () -> new DynamicClassLoader(jars.toArray(new URL[jars.size()])) ); }
tmpDir = tmpDir + File.separator + "apache-jmeter-5.3";
JMETER_INSTALLATION_DIRECTORY=tmpDir;
// ClassFinder needs the classpath
System.setProperty(JAVA_CLASS_PATH, initiaClasspath + classpath.toString());
loader = AccessController.doPrivileged(
(PrivilegedAction<DynamicClassLoader>) () ->
new DynamicClassLoader(jars.toArray(new URL[jars.size()]))
);
public class DynamicClassLoader extends URLClassLoader { public DynamicClassLoader(URL[] urls) { super(urls); } public DynamicClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } public DynamicClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) { super(urls, parent, factory); } // Make the addURL method visible @Override public void addURL(URL url) { super.addURL(url); } /** * * @param urls - list of URLs to add to the thread's classloader */ public static void updateLoader(URL [] urls) { DynamicClassLoader loader = (DynamicClassLoader) Thread.currentThread().getContextClassLoader(); for(URL url : urls) { loader.addURL(url); } } }
public static void main(String[] args) { if(!EXCEPTIONS_IN_INIT.isEmpty()) { System.err.println("Configuration error during init, see exceptions:"+exceptionsToString(EXCEPTIONS_IN_INIT)); // NOSONAR Intentional System.err use } else { Thread.currentThread().setContextClassLoader(loader); setLoggingProperties(args); try { // Only set property if it has not been set explicitely if(System.getProperty(HEADLESS_MODE_PROPERTY) == null && shouldBeHeadless(args)) { System.setProperty(HEADLESS_MODE_PROPERTY, "true"); } Class<?> initialClass = loader.loadClass("org.apache.jmeter.JMeter");// $NON-NLS-1$ Object instance = initialClass.getDeclaredConstructor().newInstance(); Method startup = initialClass.getMethod("start", new Class[] { new String[0].getClass() });// $NON-NLS-1$ 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); // NOSONAR Intentional System.err use } } }
Thread.currentThread().setContextClassLoader(loader);// loader initialized in the static code
根据启动命令行参数j和i执行日志文件路径
private static void setLoggingProperties(String[] args) { String jmLogFile = getCommandLineArgument(args, 'j', "jmeterlogfile");// $NON-NLS-1$ $NON-NLS-2$ if (jmLogFile != null && !jmLogFile.isEmpty()) { jmLogFile = replaceDateFormatInFileName(jmLogFile); System.setProperty(JMETER_LOGFILE_SYSTEM_PROPERTY, jmLogFile);// $NON-NLS-1$ } else if (System.getProperty(JMETER_LOGFILE_SYSTEM_PROPERTY) == null) {// $NON-NLS-1$ System.setProperty(JMETER_LOGFILE_SYSTEM_PROPERTY, "jmeter.log");// $NON-NLS-1$ $NON-NLS-2$ } String jmLogConf = getCommandLineArgument(args, 'i', "jmeterlogconf");// $NON-NLS-1$ $NON-NLS-2$ File logConfFile = null; if (jmLogConf != null && !jmLogConf.isEmpty()) { logConfFile = new File(jmLogConf); } else if (System.getProperty("log4j.configurationFile") == null) {// $NON-NLS-1$ logConfFile = new File("log4j2.xml");// $NON-NLS-1$ if (!logConfFile.isFile()) { logConfFile = new File(JMETER_INSTALLATION_DIRECTORY, "bin" + File.separator + "log4j2.xml");// $NON-NLS-1$ $NON-NLS-2$ } } if (logConfFile != null) { System.setProperty("log4j.configurationFile", logConfFile.toURI().toString());// $NON-NLS-1$ } }
private static boolean shouldBeHeadless(String[] args) {
for (String arg : args) {
if("-n".equals(arg) || "-s".equals(arg) || "-g".equals(arg)) {
return true;
}
}
return false;
}
Class<?> initialClass = loader.loadClass("org.apache.jmeter.JMeter");
Object instance = initialClass.getDeclaredConstructor().newInstance();
Method startup = initialClass.getMethod("start", new Class[] { new String[0].getClass() });
startup.invoke(instance, new Object[] { args });
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 { initializeProperties(parser); // Also initialises JMeter logging Thread.setDefaultUncaughtExceptionHandler( (Thread t, Throwable e) -> { if (!(e instanceof ThreadDeath)) { log.error("Uncaught exception in thread " + t, e); System.err.println("Uncaught Exception " + e + " in thread " + t + ". 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("java.awt.headless={}", System.getProperty("java.awt.headless"));//$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, deleteResultFile); ReportGenerator generator = new ReportGenerator(reportFile, null); generator.generate(); } else if (parser.getArgumentById(NONGUI_OPT) == null) { // not non-GUI => GUI startGui(testFile); startOptionalServers(); } else { // NON-GUI must be true extractAndSetReportOutputFolder(parser, deleteResultFile); CLOption remoteTest = parser.getArgumentById(REMOTE_OPT_PARAM); if (remoteTest == null) { remoteTest = 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"); } startNonGui(testFile, jtlFile, remoteTest, 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); } }
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";
}
private void initializeProperties(CLArgsParser parser) { if (parser.getArgumentById(PROPFILE_OPT) != null) { JMeterUtils.loadJMeterProperties(parser.getArgumentById(PROPFILE_OPT).getArgument()); } else { JMeterUtils.loadJMeterProperties(NewDriver.getJMeterDir() + File.separator + "bin" + File.separator // $NON-NLS-1$ + "jmeter.properties");// $NON-NLS-1$ } JMeterUtils.initLocale(); // Bug 33845 - allow direct override of Home dir if (parser.getArgumentById(JMETER_HOME_OPT) == null) { JMeterUtils.setJMeterHome(NewDriver.getJMeterDir()); } else { JMeterUtils.setJMeterHome(parser.getArgumentById(JMETER_HOME_OPT).getArgument()); } Properties jmeterProps = JMeterUtils.getJMeterProperties(); remoteProps = new Properties(); // Add local JMeter properties, if the file is found String userProp = JMeterUtils.getPropDefault("user.properties",""); //$NON-NLS-1$ if (userProp.length() > 0){ //$NON-NLS-1$ File file = JMeterUtils.findFile(userProp); if (file.canRead()){ try (FileInputStream fis = new FileInputStream(file)){ log.info("Loading user properties from: {}", file); Properties tmp = new Properties(); tmp.load(fis); jmeterProps.putAll(tmp); } catch (IOException e) { log.warn("Error loading user property file: {}", userProp, e); } } } // Add local system properties, if the file is found String sysProp = JMeterUtils.getPropDefault("system.properties",""); //$NON-NLS-1$ if (sysProp.length() > 0){ File file = JMeterUtils.findFile(sysProp); if (file.canRead()) { try (FileInputStream fis = new FileInputStream(file)){ log.info("Loading system properties from: {}", file); System.getProperties().load(fis); } catch (IOException e) { log.warn("Error loading system property file: {}", sysProp, e); } } } // Process command line property definitions // These can potentially occur multiple times List<CLOption> clOptions = parser.getArguments(); for (CLOption option : clOptions) { String name = option.getArgument(0); String value = option.getArgument(1); switch (option.getDescriptor().getId()) { // Should not have any text arguments case CLOption.TEXT_ARGUMENT: throw new IllegalArgumentException("Unknown arg: " + option.getArgument()); case PROPFILE2_OPT: // Bug 33920 - allow multiple props log.info("Loading additional properties from: {}", name); try (FileInputStream fis = new FileInputStream(new File(name))){ Properties tmp = new Properties(); tmp.load(fis); jmeterProps.putAll(tmp); } catch (FileNotFoundException e) { // NOSONAR log.warn("Can't find additional property file: {}", name, e); } catch (IOException e) { // NOSONAR log.warn("Error loading additional property file: {}", name, e); } break; case SYSTEM_PROPFILE: log.info("Setting System properties from file: {}", name); try (FileInputStream fis = new FileInputStream(new File(name))){ System.getProperties().load(fis); } catch (IOException e) { // NOSONAR if (log.isWarnEnabled()) { log.warn("Cannot find system property file. {}", e.getLocalizedMessage()); } } break; case SYSTEM_PROPERTY: if (value.length() > 0) { // Set it log.info("Setting System property: {}={}", name, value); System.getProperties().setProperty(name, value); } else { // Reset it log.warn("Removing System property: {}", name); System.getProperties().remove(name); } break; case JMETER_PROPERTY: if (value.length() > 0) { // Set it log.info("Setting JMeter property: {}={}", name, value); jmeterProps.setProperty(name, value); } else { // Reset it log.warn("Removing JMeter property: {}", name); jmeterProps.remove(name); } break; case JMETER_GLOBAL_PROP: if (value.length() > 0) { // Set it log.info("Setting Global property: {}={}", name, value); remoteProps.setProperty(name, value); } else { File propFile = new File(name); if (propFile.canRead()) { log.info("Setting Global properties from the file {}", name); try (FileInputStream fis = new FileInputStream(propFile)){ remoteProps.load(fis); } catch (FileNotFoundException e) { // NOSONAR if (log.isWarnEnabled()) { log.warn("Could not find properties file: {}", e.getLocalizedMessage()); } } catch (IOException e) { // NOSONAR if (log.isWarnEnabled()) { log.warn("Could not load properties file: {}", e.getLocalizedMessage()); } } } } break; case LOGLEVEL: if (value.length() > 0) { // Set category log.info("LogLevel: {}={}", name, value); final Level logLevel = Level.getLevel(value); if (logLevel != null) { String loggerName = name; if (name.startsWith("jmeter") || name.startsWith("jorphan")) { loggerName = PACKAGE_PREFIX + name; } Configurator.setAllLevels(loggerName, logLevel); } else { log.warn("Invalid log level, '{}' for '{}'.", value, name); } } else { // Set root level log.warn("LogLevel: {}", name); final Level logLevel = Level.getLevel(name); if (logLevel != null) { Configurator.setRootLevel(logLevel); } else { log.warn("Invalid log level, '{}', for the root logger.", name); } } break; case REMOTE_STOP: remoteStop = true; break; case FORCE_DELETE_RESULT_FILE: deleteResultFile = true; break; default: // ignored break; } } String sampleVariables = (String) jmeterProps.get(SampleEvent.SAMPLE_VARIABLES); if (sampleVariables != null){ remoteProps.put(SampleEvent.SAMPLE_VARIABLES, sampleVariables); } jmeterProps.put("jmeter.version", JMeterUtils.getJMeterVersion()); }
private void setProxy(CLArgsParser parser) throws IllegalUserActionException { if (parser.getArgumentById(PROXY_USERNAME) != null) { Properties jmeterProps = JMeterUtils.getJMeterProperties(); if (parser.getArgumentById(PROXY_PASSWORD) != null) { String u = parser.getArgumentById(PROXY_USERNAME).getArgument(); String p = parser.getArgumentById(PROXY_PASSWORD).getArgument(); Authenticator.setDefault(new ProxyAuthenticator(u, p)); log.info("Set Proxy login: {}/{}", u, p); jmeterProps.setProperty(HTTP_PROXY_USER, u);//for Httpclient jmeterProps.setProperty(HTTP_PROXY_PASS, p);//for Httpclient } else { String u = parser.getArgumentById(PROXY_USERNAME).getArgument(); Authenticator.setDefault(new ProxyAuthenticator(u, "")); log.info("Set Proxy login: {}", u); jmeterProps.setProperty(HTTP_PROXY_USER, u); } } if (parser.getArgumentById(PROXY_HOST) != null && parser.getArgumentById(PROXY_PORT) != null) { String h = parser.getArgumentById(PROXY_HOST).getArgument(); String p = parser.getArgumentById(PROXY_PORT).getArgument(); System.setProperty("http.proxyHost", h );// $NON-NLS-1$ System.setProperty("https.proxyHost", h);// $NON-NLS-1$ System.setProperty("http.proxyPort", p);// $NON-NLS-1$ System.setProperty("https.proxyPort", p);// $NON-NLS-1$ String proxyScheme = null; if (parser.getArgumentById(PROXY_SCHEME) != null) { proxyScheme = parser.getArgumentById(PROXY_SCHEME).getArgument(); if(!StringUtils.isBlank(proxyScheme)){ System.setProperty("http.proxyScheme", proxyScheme );// $NON-NLS-1$ } } if(log.isInfoEnabled()) { log.info("Set proxy Host: {}, Port: {}, Scheme: {}", h, p, proxyScheme != null ? proxyScheme : "Not set"); } } else if (parser.getArgumentById(PROXY_HOST) != null || parser.getArgumentById(PROXY_PORT) != null) { throw new IllegalUserActionException(JMeterUtils.getResString("proxy_cl_error"));// $NON-NLS-1$ } if (parser.getArgumentById(NONPROXY_HOSTS) != null) { String n = parser.getArgumentById(NONPROXY_HOSTS).getArgument(); System.setProperty("http.nonProxyHosts", n );// $NON-NLS-1$ System.setProperty("https.nonProxyHosts", n );// $NON-NLS-1$ log.info("Set http[s].nonProxyHosts: {}", n); } }
// Update classloader if necessary
private void updateClassLoader() throws MalformedURLException {
updatePath("search_paths",";", true); //$NON-NLS-1$//$NON-NLS-2$
updatePath("user.classpath",File.pathSeparator, true);//$NON-NLS-1$
updatePath("plugin_dependency_paths",";", false);//$NON-NLS-1$
}
Start the server
// 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;
}
Start the GUI
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, deleteResultFile); ReportGenerator generator = new ReportGenerator(reportFile, null); generator.generate(); } else if (parser.getArgumentById(NONGUI_OPT) == null) { // not non-GUI => GUI startGui(testFile); startOptionalServers();
No GUI
extractAndSetReportOutputFolder(parser, deleteResultFile); CLOption remoteTest = parser.getArgumentById(REMOTE_OPT_PARAM); if (remoteTest == null) { remoteTest = 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"); } startNonGui(testFile, jtlFile, remoteTest, reportAtEndOpt != null); startOptionalServers();
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); }
// run test in batch mode void runNonGui(String testFile, String logFile, boolean remoteStart, String remoteHostsString, boolean generateReportDashboard) throws ConfigurationException { try { File f = new File(testFile); if (!f.exists() || !f.isFile()) { throw new ConfigurationException("The file " + f.getAbsolutePath() + " doesn't exist or can't be opened"); } 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 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<>(); println("Created the tree successfully using "+testFile); if (!remoteStart) { JMeterEngine engine = new StandardJMeterEngine(); clonedTree.add(clonedTree.getArray()[0], new ListenToTest( org.apache.jmeter.JMeter.ListenToTest.RunMode.LOCAL, false, reportGenerator)); engine.configure(clonedTree); long now=System.currentTimeMillis(); println("Starting standalone test @ "+new Date(now)+" ("+now+")"); engines.add(engine); engine.runTest(); } else { java.util.StringTokenizer st = new java.util.StringTokenizer(remoteHostsString.trim(), ",");//$NON-NLS-1$ List<String> hosts = new LinkedList<>(); while (st.hasMoreElements()) { hosts.add(((String) st.nextElement()).trim()); } ListenToTest testListener = new ListenToTest( org.apache.jmeter.JMeter.ListenToTest.RunMode.REMOTE, remoteStop, reportGenerator); clonedTree.add(clonedTree.getArray()[0], testListener); DistributedRunner distributedRunner=new DistributedRunner(this.remoteProps); distributedRunner.setStdout(System.out); // NOSONAR distributedRunner.setStdErr(System.err); // NOSONAR distributedRunner.init(hosts, clonedTree); engines.addAll(distributedRunner.getEngines()); testListener.setStartedRemoteEngines(engines); distributedRunner.start(); } startUdpDdaemon(engines); } catch (ConfigurationException e) { throw e; } catch (Exception e) { System.out.println("Error in NonGUIDriver " + e.toString());//NOSONAR log.error("Error in NonGUIDriver", e); throw new ConfigurationException("Error in NonGUIDriver " + e.getMessage(), e); } }
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 HashTree clonedTree = convertSubTree(tree, true);
@Override
public void runTest() throws JMeterEngineException {
if (host != null){
long now=System.currentTimeMillis();
System.out.println("Starting the test on host " + host + " @ "+new Date(now)+" ("+now+")"); // NOSONAR Intentional
}
try {
Thread runningThread = new Thread(this, "StandardJMeterEngine");
runningThread.start();
} catch (Exception err) {
stopTest();
throw new JMeterEngineException(err);
}
}
StandardJMeterEngine实现了Runnable接口,run方法是执行用例的方法
public class StandardJMeterEngine implements JMeterEngine, Runnable
@Override public void run() { log.info("Running the test!"); running = true; /* * Ensure that the sample variables are correctly initialised for each run. */ 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", e); 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); 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); TestCompiler.initialize(); // for each thread group, generate threads // hand each thread the sampler controller // and the listeners, and the timer Iterator<SetupThreadGroup> setupIter = setupSearcher.getSearchResults().iterator(); Iterator<AbstractThreadGroup> iter = searcher.getSearchResults().iterator(); Iterator<PostThreadGroup> postIter = postSearcher.getSearchResults().iterator(); ListenerNotifier notifier = new ListenerNotifier(); int groupCount = 0; JMeterContextService.clearTotalThreads(); 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); startThreadGroup(group, groupCount, setupSearcher, testLevelElements, notifier); if (serialized && setupIter.hasNext()) { log.info("Waiting for setup thread group: {} to finish before starting next setup group", groupName); 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(); } 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 || 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: {} to finish before starting next group", groupName); 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 = 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: {} to finish before starting next post group", groupName); 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); // NOSONAR Intentional } }
private void startThreadGroup(AbstractThreadGroup group, int groupCount, SearchByClass<?> searcher, List<?> testLevelElements, ListenerNotifier notifier) { try { int numThreads = group.getNumThreads(); JMeterContextService.addTotalThreads(numThreads); boolean onErrorStopTest = group.getOnErrorStopTest(); boolean onErrorStopTestNow = group.getOnErrorStopTestNow(); boolean onErrorStopThread = group.getOnErrorStopThread(); boolean onErrorStartNextLoop = group.getOnErrorStartNextLoop(); String groupName = group.getName(); log.info("Starting {} threads for group {}.", numThreads, groupName); if (onErrorStopTest) { log.info("Test will stop on error"); } else if (onErrorStopTestNow) { log.info("Test will stop abruptly on error"); } else if (onErrorStopThread) { log.info("Thread will stop on error"); } else if (onErrorStartNextLoop) { log.info("Thread will start next loop on error"); } else { log.info("Thread will continue on error"); } ListedHashTree threadGroupTree = (ListedHashTree) searcher.getSubTree(group); threadGroupTree.add(group, testLevelElements); groups.add(group); group.start(groupCount, notifier, threadGroupTree, this); } catch (JMeterStopTestException ex) { // NOSONAR Reported by log JMeterUtils.reportErrorToUser("Error occurred starting thread group :" + group.getName()+ ", error message:"+ex.getMessage() +", \r\nsee log file for more details", ex); return; // no point continuing } }
这里会判断delayedStartup,只有 delayedStartup 为true才会使用ThreadStarter去启动线程(就是界面上配置的ramp up的方式启动线程)
所以实际上通过ramp up方式启动线程组需要同时勾选上 delayedStartup
public void start(int groupNum, ListenerNotifier notifier, ListedHashTree threadGroupTree, StandardJMeterEngine engine) { this.running = true; this.groupNumber = groupNum; this.notifier = notifier; this.threadGroupTree = threadGroupTree; int numThreads = getNumThreads(); int rampUpPeriodInSeconds = getRampUp(); boolean isSameUserOnNextIteration = isSameUserOnNextIteration(); delayedStartup = isDelayedStartup(); // Fetch once; needs to stay constant log.info("Starting thread group... number={} threads={} ramp-up={} delayedStart={}", groupNumber, numThreads, rampUpPeriodInSeconds, delayedStartup); if (delayedStartup) { threadStarter = new Thread(new ThreadStarter(notifier, threadGroupTree, engine), getName()+"-ThreadStarter"); threadStarter.setDaemon(true); threadStarter.start(); // N.B. we don't wait for the thread to complete, as that would prevent parallel TGs } else { final JMeterContext context = JMeterContextService.getContext(); long lastThreadStartInMillis = 0; int delayForNextThreadInMillis = 0; final int perThreadDelayInMillis = Math.round((float) rampUpPeriodInSeconds * 1000 / numThreads); for (int threadNum = 0; running && threadNum < numThreads; threadNum++) { long nowInMillis = System.currentTimeMillis(); if(threadNum > 0) { long timeElapsedToStartLastThread = nowInMillis - lastThreadStartInMillis; delayForNextThreadInMillis += perThreadDelayInMillis - timeElapsedToStartLastThread; } if (log.isDebugEnabled()) { log.debug("Computed delayForNextThreadInMillis:{} for thread:{}", delayForNextThreadInMillis, Thread.currentThread().getId()); } lastThreadStartInMillis = nowInMillis; startNewThread(notifier, threadGroupTree, engine, threadNum, context, nowInMillis, Math.max(0, delayForNextThreadInMillis), isSameUserOnNextIteration); } } log.info("Started thread group number {}", groupNumber); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。