JMeter5.0核心源码浅析[转]
【转自: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
版权声明:本文为博主原创文章,转载请附上博文链接!
JMeter5.0核心源码浅析[转]的更多相关文章
- ConcurrentHashMap核心源码浅析
1.引子 并发编程中使用HashMap可能导致程序死循环.因为多线程会put方法添加键值对时将导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为 ...
- Android版数据结构与算法(五):LinkedHashMap核心源码彻底分析
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 上一篇基于哈希表实现HashMap核心源码彻底分析 分析了HashMap的源码,主要分析了扩容机制,如果感兴趣的可以去看看,扩容机制那几行最难懂的 ...
- 并发编程之 SynchronousQueue 核心源码分析
前言 SynchronousQueue 是一个普通用户不怎么常用的队列,通常在创建无界线程池(Executors.newCachedThreadPool())的时候使用,也就是那个非常危险的线程池 ^ ...
- iOS 开源库系列 Aspects核心源码分析---面向切面编程之疯狂的 Aspects
Aspects的源码学习,我学到的有几下几点 Objective-C Runtime 理解OC的消息分发机制 KVO中的指针交换技术 Block 在内存中的数据结构 const 的修饰区别 block ...
- Backbone事件机制核心源码(仅包含Events、Model模块)
一.应用场景 为了改善酷版139邮箱的代码结构,引入backbone的事件机制,按照MVC的分层思想搭建酷版云邮局的代码框架.力求在保持酷版轻量级的基础上提高代码的可维护性. 二.遗留问题 1.b ...
- 6 手写Java LinkedHashMap 核心源码
概述 LinkedHashMap是Java中常用的数据结构之一,安卓中的LruCache缓存,底层使用的就是LinkedHashMap,LRU(Least Recently Used)算法,即最近最少 ...
- 3 手写Java HashMap核心源码
手写Java HashMap核心源码 上一章手写LinkedList核心源码,本章我们来手写Java HashMap的核心源码. 我们来先了解一下HashMap的原理.HashMap 字面意思 has ...
- 2 手写Java LinkedList核心源码
上一章我们手写了ArrayList的核心源码,ArrayList底层是用了一个数组来保存数据,数组保存数据的优点就是查找效率高,但是删除效率特别低,最坏的情况下需要移动所有的元素.在查找需求比较重要的 ...
- 1 手写Java ArrayList核心源码
手写ArrayList核心源码 ArrayList是Java中常用的数据结构,不光有ArrayList,还有LinkedList,HashMap,LinkedHashMap,HashSet,Queue ...
随机推荐
- Fiddler实现iPhone手机抓包
最近某小程序大火,许多非专业人员也在跃跃欲试,但是在查找自己的session_id的时候卡住了,本文只从技术方面介绍如何通过通过Fiddler来抓取手机的数据,不涉及如何作弊... 1.电脑上安装Fi ...
- zencart只有购买过此产品的客户才能评价产品
当前登录的客户买过此产品时,才显示评价按钮: <?php $rev_query = "select count(*) as count from orders o ,orders_pr ...
- 获取页面url信息
方法: window.location.href = prefixURL+'webstatic/messageAnalysis/datadetail.html?id=' + num + "& ...
- MyEclipse使用教程:使用工作集组织工作区
[MyEclipse CI 2019.4.0安装包下载] 工作集允许您通过过滤掉不关注的项目来组织项目视图.激活工作集时,只有分配给它的项目才会显示在项目视图中. 如果您的视图中有大量项目,这将非常有 ...
- chr ord 去重
找不同字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母. 请找出在 t 中被添加的字母. def func(s, t): num1 = 0 num2 = 0 for i in s: nu ...
- META标签的设置
㈠定义及用法 ⑴<meta> 元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词. ⑵<meta> 标签位于文档的头部,不 ...
- #5 DIV2 A POJ 3321 Apple Tree 摘苹果 构建线段树
Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 25232 Accepted: 7503 Descr ...
- 洛谷P3294 [SCOI2016]背单词——题解
题目传送 阅读理解题题意解释可以看这位大佬的博客. 发现求后缀与倒序求前缀是等价的,而找前缀自然就想到了trie树.将所有字符串翻转后再建入trie树中,再对每一个字符串翻转后从trie树中找前缀,就 ...
- [CF666E]Forensic Examination:后缀自动机+线段树合并
分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...
- Java并发编程的艺术笔记(七)——CountDownLatch、CyclicBarrier详解
一.等待多线程完成的CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作,像加强版的join.(t.join()是等待t线程完成) 例: (1)开启多个线程 ...