JMeter 源码解读 [5] - 核心类 org.apache.jmeter.JMeter
上一篇 程序入口 NewDriver 提到Main函数是通过反射的方式调用org.apache.jmeter.JMeter 的 start函数来启动Jmeter ,那么今天我们来看看Jmeter 这个类是怎么工作的.
我们从start 函数来开始分析,由于外层的NewDriver 可以看做一层透明的代理,实际的业务逻辑是透传到JMeter来处理的,首先就是解析启动时候传入的外部命令行参数
1 | CLArgsParser parser = new CLArgsParser(args, options); |
我们看到上面的代码的主要工作是解析命令行参数,并且判断有没有存在矛盾的组合,例如GUI 相关的参数,但是实际的运行模式是非GUI 模式等。
各种相关解析的任务完成后,有一个比较重要的工作updateClossLoader的调用,它是JMeter类自身的一个方法,我们可以来看一下
1 | private void updateClassLoader() throws MalformedURLException { |
它的实际作用是把search_paths和user.classpath里面设置的 jar 文件添加到自定义的classloader 中,它的作用可以让我们在某些场景下更方便使用JMeter,例如我们自定义的Java Sampler,在官方的guide里说是要把Java Sampler 打包成 Jar 放到lib/ext 目录下,这个很麻烦,实际上我们可以通过设置search_paths 和user.classpath的方式来简化操作。
然后我们再往下看,跳过一些处理特定option的code ,最核心的就是如下两个核心调用,一个是启动 GUI 的模式, 这里传入的testFile 参数就是我们的 jmx 测试 plan 文件路径
1 | startGui(testFile); |
另外一种就是非 GUI 模式
1 | startNonGui(testFile, jtlFile, rem, reportAtEndOpt != null); |
startOptionalServers() 是一个独立的BeanShell server, 用来方便对 Jmeter 进程做一些动态调整,以后可以单独写一个来介绍beanshell server 相关的东西, 这里先不关注了。GUI 的模式在启动过后就能看到界面了(一个 swing 的 app)等待用户的输入,所以我们先看一下startNonGui 这个方法, 这个方法的核心是创建了一个新的JMeter driver (源码里自己也有一个疑问,why does it create a new instance?) ,在非分布式的场景下,实际最后是调用了
1 | private void startNonGui(String testFile, String logFile, CLOption remoteStart, boolean generateReportDashboard) |
那么我们再来看runNonGui 在干什么,它是真的要开始跑性能测试了,首先是对于 TestPlan 的 HashTree 的解析,获取需要的相关信息和再做一些初始化,如何解析HashTree这个我们在之前的 HashTree 中已经介绍过了, 最后的核心是调用如下代码来启动 JMeter 测试
1 | if (!remoteStart) { |
我们看到首先会区分是不是 Remote 模式,我们这里只看非Remote的场景。engine.runTest() 这个是一个异步的调用,实际是启动一个线程,这个具体下次谈StandardJmeterEngine 的时候来看
startUdpDdaemon(engines) 是一个JMeter管理服务,用来管理 Jmeter 的运行状况,例如Shutdown/StopTestNow/HeapDump/ThreadDump 等,这个样的设计让整个JMeter的管理和JMeter自身的进程实现了解耦。
好,下一次我们来看一下 StandardJMeterEngine的实现