Jmeter源码系列(2) - Jmeter 类详解-命令行参数处理CLOptionDescriptor

上一篇我们详细了解了 Jmeter 的启动类 NewDriver,知道了 NewDriver 会通过反射调用 Jmeter.start(String[] args)方法来启动 Jmeter,今天我们来分析下,Jmeter这个类内部到底做了什么。本篇章不会直接开始讲 start 方法,而是会先讲一下 Jmeter 类里面设置的 static 变量,因为这些变量会影响 jmeter 启动时的一些行为。

Jmeter 类的作用

Jmeter类位于 org.apache.jmeter 包下,通过类注释可以了解到它的作用

1
2
3
/**
* 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 里面也会打印如下内容:

1
2
3
4
5
6
7
8
================================================================================
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 个关键内容:

  1. 调试 jmx 脚本的时候可以使用有界面模式,如果要进行测试的话,建议我们使用CLI模式,即无界面模式。
  2. 告诉用户如何配置 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 中定义了这么多变量有啥用,当然,我不会把每个变量都解释一遍,只会对关键部分做解释说明。在这些变量中,有很多变量是类似于以下这种:

1
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 模式下,解析参数用的。比如当我们使用命令

1
jmeter -h

就可以看到以下输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    _    ____   _    ____ _   _ _____       _ __  __ _____ _____ _____ ____
/ \ | _ \ / \ / ___| | | | ____| | | \/ | ____|_ _| ____| _ \
/ _ \ | |_) / _ \| | | |_| | _| _ | | |\/| | _| | | | _| | |_) |
/ ___ \| __/ ___ \ |___| _ | |___ | |_| | | | | |___ | | | |___| _ <
/_/ \_\_| /_/ \_\____|_| |_|_____| \___/|_| |_|_____| |_| |_____|_| \_\ 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 处理之后返回的
image.png
这段代码就在 Jmeter.start(String[] args) 方法中,判断了参数列表是不是包含 ‘h’,然后打印了 org/apache/jmeter/help.txt 这个文件的内容,我们也可以打开这个文件,看下内容是不是一样的
image.png
我们可以看到,打印内容基本一致,但是少了一个 banner 图,那是因为 banner 图是在上面的 displayAsciiArt()方法中打印的,我们也可以顺便看下这段打印 banner 的代码:

1
2
3
4
5
6
7
8
9
10
11
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) 方法。