概述
TestCompiler类继承HashTreeTraverser类,重写addNode和subtractNode方法来解析jmx文件中的控制器LoopController、Sampler、前后置处理器、配置元件、定时器、断言、提取器等。
源码分析
构造函数
获取HashTree对象,这里存放的是ThreadGroup对象及其子节点列表
代码语言:txt复制 public TestCompiler(HashTree testTree) {
this.testTree = testTree;
}
主要变量
代码语言:txt复制 // 对象对
private static final Set<ObjectPair> PAIRING = new HashSet<>();
// 栈,存放线程组、Controller、Sampler、断言等对象
private final LinkedList<TestElement> stack = new LinkedList<>();
private final Map<Sampler, SamplePackage> samplerConfigMap = new HashMap<>();
private final Map<TransactionController, SamplePackage> transactionControllerConfigMap =
new HashMap<>();
private final HashTree testTree;
主要方法
initialize
在测试运行开始时,通过调用StandardJmeterEngine类来清除配对集
代码语言:txt复制 public static void initialize() {
// synch is probably not needed as only called before run starts
synchronized (PAIRING) {
PAIRING.clear();
}
}
addNode
被HashTree的traverse和traverseInTo方法调用,通过函数递归,将node对象添加到栈中。
以jmx文件的线程组及其子节点为例:
- ThreadGroup
- Arguments
- LoopController
- HTTPSamplerProxy
- RegexExtractor
首先将ThreadGroup添加到stack中,然后是Arguments,直到最后将RegexExtractor也添加到stack中
由于存储空间用的是栈,所以栈顶节点是RegexExtractor,栈底节点是ThreadGroup。
代码语言:txt复制 public void addNode(Object node, HashTree subTree) {
stack.addLast((TestElement) node);
}
subtractNode
被HashTree的traverseInTo方法调用,当执行完addNode()方法后,执行subtractNode方法。
代码语言:txt复制 public void subtractNode() {
if (log.isDebugEnabled()) {
log.debug("Subtracting node, stack size = {}", stack.size());
}
// 获取栈顶节点(后进先出)
TestElement child = stack.getLast();
trackIterationListeners(stack);
//判断是否为Sampler
if (child instanceof Sampler) {
// 获取sampler相关的配置元件,监听器,前后置处理器等对象
saveSamplerConfigs((Sampler) child);
}
// 好像用不到,跳过
else if(child instanceof TransactionController) {
System.out.println("this is a transactionController.");
saveTransactionControllerConfigs((TransactionController) child);
}
// 移除栈顶节点,即child节点
stack.removeLast();
// 判断栈是否为空
if (!stack.isEmpty()) {
// 获取栈顶节点,即parent节点
TestElement parent = stack.getLast();
// 对象对是否已存在对象的标志
boolean duplicate = false;
// Bug 53750: this condition used to be in ObjectPair#addTestElements()
// 条件1:parent节点为controller类型
// 条件2:child节点为Sampler类型或者cotroller类型
if (parent instanceof Controller && (child instanceof Sampler || child instanceof Controller)) {
// 判断类型是否为TestCompilerHelper
// 这里threadGroup和GenericController都继承TestCompilerHelper
if (parent instanceof TestCompilerHelper) {
TestCompilerHelper te = (TestCompilerHelper) parent;
// 将child添加到parent的subControllersAndSamplers列表中
duplicate = !te.addTestElementOnce(child);
} else { // this is only possible for 3rd party controllers by default
ObjectPair pair = new ObjectPair(child, parent);
synchronized (PAIRING) {// Called from multiple threads
if (!PAIRING.contains(pair)) {
parent.addTestElement(child);
PAIRING.add(pair);
} else {
duplicate = true;
}
}
}
}
if (duplicate) {
if (log.isWarnEnabled()) {
log.warn("Unexpected duplicate for {} and {}", parent.getClass(), child.getClass());
}
}
}
}
saveSamplerConfigs
获取sampler的公共的配置元件、监听器、定时器,循环控制器等组件节点,也包括其下的所有子组件节点(前后置处理器,断言,提取器等)
代码语言:txt复制 private void saveSamplerConfigs(Sampler sam) {
// 存放配置元件的列表
List<ConfigTestElement> configs = new LinkedList<>();
// 存放controller的列表
List<Controller> controllers = new LinkedList<>();
// 存放监听器的列表
List<SampleListener> listeners = new LinkedList<>();
// 存放定时器的列表
List<Timer> timers = new LinkedList<>();
// 存放断言的列表
List<Assertion> assertions = new LinkedList<>();
// 存放前置处理器的列表
LinkedList<PostProcessor> posts = new LinkedList<>();
// 存放后置处理器的列表
LinkedList<PreProcessor> pres = new LinkedList<>();
//循环遍历获取栈中的每个节点,后进先出
for (int i = stack.size(); i > 0; i--) {
// 获取当前sam的所有父controller节点,比如嵌套的LoopController、IFController和ThreadGroup
addDirectParentControllers(controllers, stack.get(i - 1));
List<PreProcessor> tempPre = new LinkedList<>();
List<PostProcessor> tempPost = new LinkedList<>();
List<Assertion> tempAssertions = new LinkedList<>();
/**
* stack.subList(0, i):获取0到i之间stack的元素列表
* testTree.list():遍历subList列表,获取其最后一个节点的子对象列表,其实就是栈顶节点的子对象列表
*/
for (Object item : testTree.list(stack.subList(0, i))) {
/**
* 两层for循环结合使用,获取公共的配置元件、监听器、定时器,循环控制器等组件节点,也包括当前sam下的所有子组件节点(前后置处理器,断言,提取器等)
*
*/
if (item instanceof ConfigTestElement) {
configs.add((ConfigTestElement) item);
}
if (item instanceof SampleListener) {
listeners.add((SampleListener) item);
}
if (item instanceof Timer) {
timers.add((Timer) item);
}
if (item instanceof Assertion) {
tempAssertions.add((Assertion) item);
}
if (item instanceof PostProcessor) {
tempPost.add((PostProcessor) item);
}
if (item instanceof PreProcessor) {
tempPre.add((PreProcessor) item);
}
}
assertions.addAll(0, tempAssertions);
pres.addAll(0, tempPre);
posts.addAll(0, tempPost);
}
// 存储当前sam的配置元件、监听器、定时器、断言、前后置处理器,控制器等的类
SamplePackage pack = new SamplePackage(configs, listeners, timers, assertions,
posts, pres, controllers);
// 存储当前sam
pack.setSampler(sam);
// 使SamplePackage成为正在运行的版本,或使其不再是正在运行的版本。
// 这告诉SamplePackage的每个元素它的当前状态必须是可通过调用recoverRunningVersion()检索。
pack.setRunningVersion(true);
// 键值对存储
samplerConfigMap.put(sam, pack);
}
configureSampler
根据sampler获取SamplePackage对象,主要用于提取控制器、定时器、前后置处理器,断言等
代码语言:txt复制 public SamplePackage configureSampler(Sampler sampler) {
SamplePackage pack = samplerConfigMap.get(sampler);
pack.setSampler(sampler);
configureWithConfigElements(sampler, pack.getConfigs());
return pack;
}
configureWithConfigElements
将ConfigTestElement添加到Sampler中,作为Sampler的属性配置,主要作用于HTTPSamplerBase,用于添加Cookie,Header,Auth等
代码语言:txt复制 private void configureWithConfigElements(Sampler sam, List<ConfigTestElement> configs) {
// 只适用于HTTPSamplerBase,用于清除header_manager列表
sam.clearTestElementChildren();
for (ConfigTestElement config : configs) {
// 配置元件的类型不是CSVDataSet和RandomVariableConfig
if (!(config instanceof NoConfigMerge))
{
// 判断Sampler的类型是否为ConfigMergabilityIndicator
if(sam instanceof ConfigMergabilityIndicator) {
// 判断ConfigTestElement的gui_class名能否在Sampler的APPLIABLE_CONFIG_CLASSES找到,找到为true,找不到为false
if(((ConfigMergabilityIndicator)sam).applies(config)) {
// 将ConfigTestElement添加到Sampler中,作为Sampler的属性配置
// 主要作用于HTTPSamplerBase,用于添加Cookie,Header,Auth等
sam.addTestElement(config);
}
} else {
// Backward compatibility
sam.addTestElement(config);
}
}
}
}
addDirectParentControllers
获取当前sam的所有父controller节点,比如嵌套的LoopController、IFController和ThreadGroup等
代码语言:txt复制 private void addDirectParentControllers(List<Controller> controllers, TestElement maybeController) {
if (maybeController instanceof Controller) {
log.debug("adding controller: {} to sampler config", maybeController);
controllers.add((Controller) maybeController);
}
}