Jmeter源码系列(2) - Jmeter 类详解-命令行参数处理CLOptionDescriptor
上一篇我们详细了解了 Jmeter 的启动类 NewDriver,知道了 NewDriver 会通过反射调用 Jmeter.start(String[] args)方法来启动 Jmeter,今天我们来分析下,Jmeter这个类内部到底做了什么。本篇章不会直接开始讲 start 方法,而是会先讲一下 Jmeter 类里面设置的 static 变量,因为这些变量会影响 jmeter 启动时的一些行为。
Jmeter 类的作用
Jmeter类位于 org.apache.jmeter 包下,通过类注释可以了解到它的作用
/**
* Main JMeter class; processes options and starts the GUI, non-GUI or server as appropriate.
*/
Jmeter.class 是 Jmeter 的主要类,是为了让 Jmeter 通过 GUI,NON-GUI 或者server模式启动。通过我们使用 Jmeter 工具也能发现,Jmeter 正常情况下启动会有用户界面,方便我们编写 jmx 脚本或者调试 jmx 脚本。但是也可以通过 jmeter -n 模式来启动命令行模式(此处应该是无界面模式更合适)执行 jmx 脚本,并且在 Jmeter 启动时,console 里面也会打印如下内容:
================================================================================
Don't use GUI mode for load testing !, only for Test creation and Test debugging.
For load testing, use CLI Mode (was NON GUI):
jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
& increase Java Heap to meet your test requirements:
Modify current env variable HEAP="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" in the jmeter batch file
Check : https://jmeter.apache.org/usermanual/best-practices.html
================================================================================
这段内容其实包含 2 个关键内容:
- 调试 jmx 脚本的时候可以使用有界面模式,如果要进行测试的话,建议我们使用CLI模式,即无界面模式。
- 告诉用户如何配置 Jmeter 的堆内存,因为 Jmeter 本身是基于 Java 开发,也是运行在 JVM 虚拟机上的,所以如果我们进行性能测试前,可以适当调整堆内存,来防止测试过程中发生 OOM 等异常。
除了有界面和无界面两种启动模式,Jmeter 还有一种server模式,即集群模式。Jmeter 本身是支持分布式压测的,当单机的并发能力存在瓶颈的时候,可以通过配置 slave 节点来实现分布式压测,这个时候,Jmeter 就是以 server 模式启动的。
Jmeter 中的 CLOptionDescriptor
打开 org.apache.jmeter.Jmeter.java 源码,我们会发现,这个类内部定义了几十个静态变量,而且这个类还实现了一个 JMeterPlugin 接口。我们先看JMeterPlugin接口,此接口内部只有两个方法
- String[][] getIconMappings();
- String[][] getResourceBundles();
第一个 getIconMappings() 方法用于获取插件中的图标映射信息。返回一个二维字符串数组,每个数组元素包含两个字符串:图标名称和图标文件的路径。这些图标文件可以用于在 JMeter 用户界面中显示插件的图标。通过实现 getIconMappings() 方法并返回相应的图标映射,插件可以将自定义的图标与插件相关联,并在 JMeter 中展示出来,以提供更好的用户体验和可视化效果。我们使用 Jmeter 的时候就能发现,每个组件前面都会带个小图标,就是通过这个方法来去加载这些图标的。
第二个 getResourceBundles() 其实更容易理解,如果大家开发过 web 项目,知道 resourceBundles 是啥,没错,就是用来做国际化的。这个方法用于获取插件中的资源绑定信息。返回一个二维字符串数组,每个数组元素包含两个字符串:资源包的基本名称和资源包的位置。资源包是包含本地化文本消息、错误消息、标签等的文件集合,用于国际化和本地化插件的用户界面。通过实现 getResourceBundles() 方法并返回相应的资源绑定信息,插件可以实现多语言支持,并根据用户的语言环境动态加载适当的本地化资源。
接下来,我们看下 Jmeter 中定义了这么多变量有啥用,当然,我不会把每个变量都解释一遍,只会对关键部分做解释说明。在这些变量中,有很多变量是类似于以下这种:
private static final int REMOTE_OPT_PARAM = 'R';
大家要注意,这个变量类型是 int,并不是 char,因为这个变量其实是代表了'R'的 ASCII 编码值(十进制数)也就是:82。这种做法是为了提高代码的可读性和可维护性。通过使用命名的常量,代码的其他部分可以直接使用 REMOTE_OPT_PARAM 来表示这个特定的值,而不是使用硬编码的字符 'R' 或数字 82。这样,如果将来需要更改这个值,只需修改常量的定义即可,而不需要对代码中所有引用到这个值的地方进行修改。
这种定义的变量,会被一个叫 CLOptionDescriptor 的类解析,我们先看下这个类它的作用是什么,其实第一眼看到这个类名的时候,就能大概猜出来,这是用来解析命令行参数的。它其实是 Apache Commons CLI 库中的一个类,作用就是解析命令行参数,提供了定义选项的名称、别名、描述、参数属性和行为的方法。这个类具体的作用如下:
- 描述选项的名称和别名:CLOptionDescriptor 允许您定义选项的名称、短名称和长名称等标识符。通过这些标识符,您可以在命令行中识别并指定特定的选项。
- 指定选项的描述信息:CLOptionDescriptor 允许您为选项提供文本描述或帮助信息,以帮助用户理解该选项的作用和用法。
- 指定选项的参数属性:CLOptionDescriptor 可以定义选项是否需要参数以及参数的类型。它支持定义选项是否需要参数、参数的最少和最多出现次数、参数的默认值等属性。
- 配置选项的行为:CLOptionDescriptor 提供了一些方法来配置选项的行为。例如,您可以定义选项是否为必需选项、是否允许多次使用、是否支持可变参数数量等。
所以,这个类的作用,就是在 CLI 模式下,解析参数用的。比如当我们使用命令
jmeter -h
就可以看到以下输出:
_ ____ _ ____ _ _ _____ _ __ __ _____ _____ _____ ____
/ \ | _ \ / \ / ___| | | | ____| | | \/ | ____|_ _| ____| _ \
/ _ \ | |_) / _ \| | | |_| | _| _ | | |\/| | _| | | | _| | |_) |
/ ___ \| __/ ___ \ |___| _ | |___ | |_| | | | | |___ | | | |___| _ <
/_/ \_\_| /_/ \_\____|_| |_|_____| \___/|_| |_|_____| |_| |_____|_| \_\ 5.4.1
Copyright (c) 1999-2021 The Apache Software Foundation
To list all command line options, open a command prompt and type:
jmeter.bat(Windows)/jmeter.sh(Linux) -?
--------------------------------------------------
To run Apache JMeter in GUI mode, open a command prompt and type:
jmeter.bat(Windows)/jmeter.sh(Linux) [-p property-file]
--------------------------------------------------
To run Apache JMeter in NON_GUI mode:
Open a command prompt (or Unix shell) and type:
jmeter.bat(Windows)/jmeter.sh(Linux) -n -t test-file [-p property-file] [-l results-file] [-j log-file]
--------------------------------------------------
To run Apache JMeter in NON_GUI mode and generate a report at end :
Open a command prompt (or Unix shell) and type:
jmeter.bat(Windows)/jmeter.sh(Linux) -n -t test-file [-p property-file] [-l results-file] [-j log-file] -e -o [Path to output folder]
--------------------------------------------------
To generate a Report from existing CSV file:
Open a command prompt (or Unix shell) and type:
jmeter.bat(Windows)/jmeter.sh(Linux) -g [csv results file] -o [path to output folder (empty or not existing)]
--------------------------------------------------
To tell Apache JMeter to use a proxy server:
Open a command prompt and type:
jmeter.bat(Windows)/jmeter.sh(Linux) -H [your.proxy.server] -P [your proxy server port]
---------------------------------------------------
To run Apache JMeter in server mode:
Open a command prompt and type:
jmeter-server.bat(Windows)/jmeter-server(Linux)
---------------------------------------------------
那么这些输出是哪里来的呢?很明显不是 CLOptionDescriptor 打印的,因为这个类的作用就是解析长短参数,还有参数提示,并不会给出命令执行的结果,我们翻一下代码就能看到,其实这个命令的结果是被 Jmeter 处理之后返回的 这段代码就在 Jmeter.start(String[] args) 方法中,判断了参数列表是不是包含 'h',然后打印了 org/apache/jmeter/help.txt 这个文件的内容,我们也可以打开这个文件,看下内容是不是一样的 我们可以看到,打印内容基本一致,但是少了一个 banner 图,那是因为 banner 图是在上面的 displayAsciiArt()方法中打印的,我们也可以顺便看下这段打印 banner 的代码:
private void displayAsciiArt() {
try (InputStream inputStream = JMeter.class.getResourceAsStream("jmeter_as_ascii_art.txt")) {
if(inputStream != null) {
String text = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
System.out.println(text);//NOSONAR
}
} catch (Exception e1) { //NOSONAR No logging here
System.out.println(JMeterUtils.getJMeterCopyright());//NOSONAR
System.out.println("Version " + JMeterUtils.getJMeterVersion());//NOSONAR
}
}
代码其实很简单,就是读了一个文件,然后输出到控制台,仅此而已。所以,综上所述,Jmeter 这个类,内部的静态变量其实就是在解析命令行参数,最后这些参数会被存储在 CLOptionDescriptor[] options 对象中,这个 option 对象非常重要,因为在 jmeter 真正启动前,会从 option 中获取好几个参数,来决定使用何种启动方式。
Jmeter 支持的命令行参数
短命令 | 长命令 | 说明 |
---|---|---|
-h |
--help |
显示帮助信息。 |
-v |
--version |
显示 JMeter 版本信息。 |
-n |
--nongui |
以非 GUI (无界面)模式运行 JMeter。 |
-t <文件名> |
--testfile <文件名> |
指定要执行的 JMX 测试计划文件。 |
-l <文件名> |
--logfile <文件名> |
指定测试结果的日志文件名。 |
-j <文件名> |
--jmeterlogfile <文件名> |
指定 JMeter 的日志文件名。 |
-r |
--runremote |
以远程方式运行测试计划,用于分布式测试。 |
-R <远程主机列表> |
--remotestart <远程主机列表> |
通过指定远程主机列表,以分布式方式运行测试计划。 |
-G <属性文件> |
--globalproperties <属性文件> |
指定全局属性文件。 |
-D <name>=<value> |
--systemproperty <name>=<value> |
设置额外的系统属性。 |
-S |
--systemPropertiesFile <文件名> |
指定系统属性文件。 |
-P <name>=<value> |
--jmeterproperty <name>=<value> |
设置 JMeter 属性值。此处参数是小写 |
-H <代理主机> |
--proxyHost <代理主机> |
指定代理服务器的主机名。 |
-P <代理端口> |
--proxyPort <代理端口> |
指定代理服务器的端口号。此处参数是大写 |
-N <非代理的主机列表> |
--nonProxyHosts <非代理的主机列表> |
指定不需要代理的主机。 |
-X |
--remoteexit |
告知远程服务器在测试结束后退出。 |
-H |
--help-report |
显示关于报告生成的帮助信息。 |
-L |
--loglevel |
指定 JMeter 日志的级别。 |
-q <属性文件> |
--addprop <属性文件> |
指定要加载的其他 JMeter 属性文件。 |
-s |
--server |
以服务器模式运行 JMeter 使用 JMeter 远程实例。 |
-f |
--forceDeleteResultFile |
在运行之前强制删除已存在的测试结果文件。 |
-i |
--ignorelineendings |
忽略测试计划文件中的行结束标记。 |
-H |
--useSystemProxy |
使用系统代理设置。 |
后面一章正式讲解 Jmeter.start(String[] args) 方法。