Jmeter源码系列(3) - Jmeter 类详解-start方法,Jmeter 真正的启动过程

在开始讲 start 方法前,大家先考虑一个问题,Jmeter 启动时带不带参数有什么影响?我们可以带着这个问题往下看。

Jmeter启动参数是如何传递的?

在前面两篇文章中,已经跟大家非常详细的介绍了 Jmeter 启动时会做什么,回顾一下就是:

  1. 调用 NewDriver.main(String[] args) 方法,此方法会反射调用 Jmeter.start(String[] args) 方法。
  2. 在执行 Jmeter.start(String[] args) 方法前,Jmeter 类现需要实例化,这个类中包含了非常多的静态常量,绝大多数都是跟启动参数相关,这些参数会在类实例化时也被初始化掉。

那么这些参数是从哪来的呢?其实入口就是 NewDriver.main(String[] args) ,这个方法的参数列表可以从 Jmeter 的启动脚本获取,也可以通过执行

1
java -jar ApacheJMeter.jar xxx 来启动

后面的 xxx 表示的就是启动参数,跟执行以下命令效果一样

1
jmeter xxx

我们可以通过查看 Jmeter /bin 目录下的 jmeter 脚本来验证这个过程,以下内容是 JMeter 在 mac 下的启动脚本,其中只保留了最后几行内容,其他系统下的脚本内容也是类似,就不在此处展开解释。
image.png
这段脚本其实很简单,就是判断 JMETER_COMPLETE_ARGS 这个变量是不是为空,用来控制 ARGS 参数值,最后一行就是很普通的 java 应用启动命令,后面的 “$@” 是一个特殊变量,用于表示所有向脚本传递的位置参数(命令行参数)。具体来说,$@ 会将所有的位置参数作为一个单独的字符串列表返回。每个位置参数会被当作一个独立的字符串,在使用时可以通过遍历 $@ 获取每个参数。需要注意的是,”$@” 使用双引号括起来时,会将每个位置参数作为独立的字符串处理,保留参数中的空格和特殊字符。这样可以确保传递参数时的正确性,在处理包含空格或特殊字符的参数时非常有用。

顺便提一句,上面判断了 JMETER_COMPLETE_ARGS 是为了给 Java9 单独设置一些启动参数,因为在 Java 9 之前的版本中,可以使用标准的 JAVA_OPTS 环境变量来设置 Java 虚拟机的选项。然而,从 Java 9 开始,Oracle 官方建议使用 JAVA_TOOL_OPTIONS 环境变量来设置 Java 应用程序的选项,以便与新的模块化系统和命令行标志兼容。为了确保 JMeter 在不同版本的 Java 中都能正常运行,并且可以方便地配置 Java 9 相关的选项,此处单独处理了 Java 9 的选项设置。通过使用 JAVA9_OPTS 环境变量存储 Java 9 相关的选项,并将其与其他选项拼接到 ARGS 字符串中,以便将这些选项传递给 JMeter 启动脚本。这种单独处理 Java 9 的选项,能够更好地适应不同版本的 Java,并确保在升级或切换 Java 版本时不会影响到 JMeter 的启动脚本和选项设置。这样做的目的是为了提高 JMeter 的兼容性和可移植性。

ok,那现在我们知道了,NewDeriver.main(String[] args) 会接受命令行参数,然后 main 方法再反射调用 Jmeter.start(String[] args) 时,也会把参数传递下去,代码如图所示:
image.png
invoke 方法第二个参数 args 就是 main 方法接收到的参数,这就完成了参数从命令行传递给 Jmeter 的过程,在实例化 Jmeter 时,这些参数又被 CLOptionDescriptor 类处理了一次,从一堆字符串变成了一个对象,方便 Jmeter 更好的处理参数,那接下来我们迎来了本文章的重点:Jmeter.start(String[] args) 方法到底做了什么?

Jmeter 是如何启动的?

在解释代码之前,大家有没有发现一个现象:

当我们什么启动参数都不带时,Jmeter 会直接以 GUI 模式启动,我们可以写脚本调试,甚至直接开始测试,也可以不跑测试,写个脚本就把它关掉。但是当我们使用 cli 时,就可以直接传递一个 jmx 脚本给 Jmeter,这时 Jmeter 就会直接开始测试。这两种方式是如何实现的呢?

如果大家有观察过这个现象,那接下来的内容会非常容易理解,因为这涉及到了 Jmeter 的 2 种启动模式:

  1. startGui
  2. startNonGui

但是这个时候大家会疑惑,之前不是说 Jmeter 有三种启动模式么,分别是:GUI,NON-GUI, SERVER,为啥到这边又成了 2 种了?

其实,这前后并没有矛盾,因为 SERVER 和 NON-GUI 模式都是属于 NON-GUI 的方式启动,我们此处只是讨论 Jmeter 在启动时的宏观表现,即有没有图形界面。

接下来,我们就开始从代码出发,看下 Jmeter 真正的启动过程。

1.命令行参数校验

image.png
在进入 start 方法后,会先对命令行参数的组合进行判断,如果参数组合不支持,则生成一个 error 信息,然后判断 error 是否为 null,如果有错误信息,则停止启动,并在控制台输出错误信息。

2.初始化运行环境

参数校验通过之后,Jmeter 会进行运行环境初始化,虽然 NewDriver 已经初始化过一次(主要做类加载),但是 Jmeter 会做更细致的初始化动作,以下是 Jmeter 初始化内容
image.png

  1. 初始化 Properties:实现方法为 Jmeter.initializeProperties(CLArgsParser parser) 方法。首先检查用户是否通过 -p 参数设置了 property 文件,如果没有设置,则直接使用 bin/jmeter.properties 文件。其次设置 Jmeter 语言环境,设置 JmeterHome,还有就是读取 user.properties,system.properties 以及用户通过命令行参数自己指定的 property 文件,最后设置下日志等级。
  2. 添加安全提供程序:根据给定的 Properties 对象,筛选匹配特定模式的键值对,并按照键的顺序逐个调用 addSecurityProvider 方法,作用是:(1)扩展功能:通过添加安全提供程序,可以扩展 Java 程序的加密、签名、哈希等安全功能,使其支持更多的算法和标准。这样,程序就可以使用更多安全服务来满足特定的需求。(2)第三方库或协议支持:有些第三方库或协议可能需要特定的加密、签名或认证机制。通过添加相应安全提供程序,可以为这些库或协议提供所需的支持,确保程序能够与它们进行兼容性的交互。(3)安全策略和规范:在某些情况下,出于安全策略和规范的考虑,需要使用特定的安全提供程序来确保符合特定的安全要求。通过添加这些提供程序,可以实施和遵循特定的安全标准。
  3. 设置默认的未捕获异常处理器:当发生未捕获异常时,使用注册的异常处理器进行处理,即在控制台输出异常信息。
  4. 设置代理:因为用户可以通过命令行参数设置代理服务器,代码比较简单,就不展开讲解了,知道是做什么就行了。
  5. 更新加载的类:又去把一堆类加载进来,核心还是调用 NewDriver.addURL(path); 方法。此处不展开讲解。
  6. 设置几个属性:设置开始时间,开始日期,以及开始的时分秒,但是感觉没啥鸟用,连注释都写了:Set some (hopefully!) useful properties,意思就是希望这几个参数你能用得上。

3.Jmeter 真正的启动

image.png

  1. 首先,判断用户有没有使用 -s 参数,如果用了这个参数,则使用 Server 模式启动。下面还有个 startOptionalServers(); 方法,其实不管是哪种启动方式,都会执行这个方法,它的作用就是根据配置启动可选的 Beanshell 服务器和 Mirror 服务器。Beanshell 服务器提供了自定义逻辑的执行能力,而 Mirror 服务器则用于模拟外部服务器行为。通过这些服务器,用户可以在测试期间执行自定义逻辑和模拟环境,以满足特定的测试需求。
    image.png
  2. 接下来判断是否使用了 -t 和 -g 两个参数,-t 用来指定 jmx 文件,-g 用来生成报告。注意,此时并没有真的把 jmx 文件加载进 Jmeter,更没有生成报告,只是做了参数的赋值操作。
    image.png
  3. 继续判断是否使用了 -n 参数,如果没有使用这个参数,则会启动图形界面。在启动图形界面前,会先调用PluginManager.install(this, true);方法来加载插件,还记得之前讲过Jmeter 实现了JMeterPlugin 接口么?作用就在此处体现了,要去加载 Jmeter 的图标和其他的资源文件。然后就会真正的启动 GUI 了,同时会在控制台打印一段熟悉的内容
    image.png
    此时,Jmeter 通过图形界面真正的启动了,当然如果你在启动前指定了 jmx 文件,那么 Jmeter 打开之后,就会默认加载这个文件,否则就是默认新建 TestPlan 的页面。
  4. 如果用户使用了 -n 参数,则说明要使用 NON-GUI 启动。启动流程也很简单,先检查下几个文件夹能不能正常写入,比如通过 -o 参数指定的报告文件夹,Jmeter 默认的报告输出文件夹等。然后就是检查是否通过 -R 指定了远程执行的机器,通过 -t 生成 jtl 文件,再检查使用了 -e 参数之后,-t 参数是不是为 null,如果没有指定要生成 jtl 文件,但是要求生成报告的话,则会抛异常,因为 Jmeter 的报告就是通过解析 jtl 文件得到的,具体是怎么生成的,会在后面的章节中跟大家探讨。最后调用 startNonGui()方法来启动无界面模式的 Jmeter。
    image.png

4.总结

至此,让我们通过一张图来了解下 Jmeter 的启动过程

image.png

此时,Jmeter 算是真正的启动起来了,因为 GUI 模式启动涉及到大量关于 Java Swing 的内容,不在本文章讨论范围内,我会主要从 NON-GUI 模式来跟大家讲解 Jmeter 的运行原理,因为两种模式本质上都是通过 Jmeter 执行引擎来实现测试的。下一章开始,我们将继续深入了解使用无界面模式启动后,Jmeter 是如何开始测试的…