JMeter 源码解读 [6] - 核心类 org.apache.jmeter.engine.StandardJMeterEngine

我们越来约越接近 JMeter 的核心,先来看一下StandardJMeterEngine 这个类实现了哪些接口,首先是JMeterEngine, 它定义了 JMeterEngine 的一些核心接口 ,其中最主要的是下面几个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 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
*/
default void stopTest() {
stopTest(true);
}

它的主要实现方,一个是单机版本的StandardJMeterEngine , 另一个是分布式模式下的ClientJmeterEngine

另外一个实现的接口是Runnable ,这个很显然我们可以猜到,StandardJMeterEngine 自身会是一个多线程执行的模式,果不其然,我们看到StandardJMeterEnginerunTest()的实现,就是把自己this 放到一个Thread 里来调用

``

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void runTest() throws JMeterEngineException {
if (host != null){
long now=System.currentTimeMillis();
System.out.println("Starting the test on host " + host + " @ "+new Date(now)+" ("+now+")"); // NOSONAR Intentional
}
try {
Thread runningThread = new Thread(this, "StandardJMeterEngine");
runningThread.start();
} catch (Exception err) {
stopTest();
throw new JMeterEngineException(err);
}
}

我们再来看一下run()的这个Runnable 接口的实现是怎么样的, 首先看到第一行代码 log.info("Running the test!"); 哈哈,总算到这里时真的开始执行case 了,哎,这一层层的调用,太艰辛了!!!

首先看一下JMeterContextService.startTest(); 这个是用来设置一个运行启动的状态,JMeterContextService 从名字来看,是一个上下文的类,它是在整个JMeter的线程之间是共享的,除了有一个threadContext 的变量,整个是一个ThreadLocal的独享,是每个线程独享的,我们简单看一些JMeterContextService 就能更清晰它的作用了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class JMeterContextService {
private static final ThreadLocal<JMeterContext> threadContext = ThreadLocal.withInitial(JMeterContext::new);

//@GuardedBy(JMeterContextService.class)
private static long testStart = 0;

//@GuardedBy(JMeterContextService.class)
private static int numberOfActiveThreads = 0;

//@GuardedBy(JMeterContextService.class)
private static int numberOfThreadsStarted = 0;

//@GuardedBy(JMeterContextService.class)
private static int numberOfThreadsFinished = 0;

再继续做了一些简答的初始化后,会先开始做用setup thread group 做一些初始化的工作 ,这里会启动 N 个线程组来做初始化,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (setupIter.hasNext()) {
log.info("Starting setUp thread groups");
while (running && setupIter.hasNext()) {//for each setup thread group
AbstractThreadGroup group = setupIter.next();
groupCount++;
String groupName = group.getName();
log.info("Starting setUp ThreadGroup: {} : {} ", groupCount, groupName);
startThreadGroup(group, groupCount, setupSearcher, testLevelElements, notifier);
if (serialized && setupIter.hasNext()) {
log.info("Waiting for setup thread group: {} to finish before starting next setup group",
groupName);
group.waitThreadsStopped();
}
}
log.info("Waiting for all setup thread groups to exit");
//wait for all Setup Threads To Exit
waitThreadsStopped();
log.info("All Setup Threads have ended");
groupCount=0;
JMeterContextService.clearTotalThreads();
}

groups.clear();

setup thread group 完成一些初始化的工作后,会主动出发一次 JMeterUtils.helpGC(); GC 的动作,后面就是执行测试的Thread Group 和 最后的Post Thread Group ,这些操作和setup thread group 的调用都差不多的,以为实际执行发送请求和并发线程的管理都在 ThreaeGroup

今天就说到这里,下次会单独来介绍一下 JMeter 的线程模型