当前位置:文档之家› JMeter 性能测试插件开发_Part1

JMeter 性能测试插件开发_Part1

JMeter 性能测试插件开发lifr(lifr_nj@)JMeter的内部数据结构和Test执行过程问题问题1:在执行完"Java Request 1" 后会等待多少秒?Test执行过程首先,我们来看看JMete内部到底是如何执行一个测试的。

执行测试,你只需要选择菜单“Run->Start"。

接下来, JMeter内部会进行相当多的动作。

理解这整个过程可以帮助你更好的利用JMeter,也是JMeter开发的基础。

JMeterStandardEngine.run()首先, JMeterStandardEngine获得控制权。

在JMeterStandarEngine.Run()函数里,有下面一些主要步骤首先会预编译TestTree,这是一个对JMeter里变量求值很重要的步骤,不过这里我们先略过。

然后会从TestTree里找出所有TestListener,并通知每一个TestListener 实例“测试开始了”然后按照ThreadGroup的参数指定的方式启动JMeterThread至此JMeterStandardEngine完成了它的工作,执行权交到了JMeterThread手上。

JMeterThread.run()在JMeterThread的run函数,有下面一些主要步骤。

首先是初始化JMeterContext, 这是一个线程执行上下文,每一个JMeterThread在运行时都有一个JMeterContext实例。

很多重要的运行时信息都保存在JMeterContext 里。

在initRun函数里对JMeterContext进行初始化。

然后会迭代Test Tree里所有的Sampler,依次执行。

在这里controller是TestTree 的ThreadGroup节点。

通过它的next()方法可以遍历所有它下面的Sampler。

实际执行在函数process_sampler里。

JMeterThread.process_sampler()在process_sampler里有下面的步骤首先获取该Sampler实例的SamplePackage。

SamplePackage是一个很重要的概念,简单起见,在这里我们只需把它理解为:把该Sampler实例相关的各种测试元素,比如变量配置,断言检查等,打包后的整体。

然后首先执行SamplePackage里的PreProcessorPreProcessor如下所示。

然后执行Timer,一般是作延时Timer如下所示。

终于,开始执行该Sampler如果result不为null,还要对PostProcessor和断言Assertion进行处理,PostProcessor如下所示Assertions如下所示由此可见,对于一个Sample 实例的执行,并不仅仅是只执行sample即可,而是前前后后执行了很多东西,这些东西都组织在SamplePackage这样一个内部数据结构里。

下面我们看看一个SamplePackage到底都有包含哪些东西,它是怎么构造出来的。

SamplePackage:Sample和它的朋友们首先, SamplePackage是由TestCompiler构造出来的。

下面是TestCompiler构造SamplePackage的函数。

那么,TestCompiler是如何构造SamplePackage的呢?这里就用到了JMeter里面很重要的一个数据结构HashTree,和对它进行遍历的接口HashTreeTraveler。

HashTree是一个描述树结构的数据结构。

在GUI里展现的TestTree在JMeter内部就是一个HashTree的实例。

HashTreeTraveler是专门用来遍历HashTree的接口。

TestCompiler实现了HashTreeTraveler接口,所以它可以遍历HashTree。

而TestCompiler遍历TestTree是发生在JMeterThread的initRun函数里。

在遍历TestTree的时候,针对每一个Sample节点,做了下面的事情。

解释如下:得到该Sample 节点到根节点的路径TreePath。

这个TreePath是node的链表。

遍历该TreePath, 方向是从根节点开始直到该Sample节点本身。

对于TreePath里的每一个node,首先获取其直接子节点children nodes。

然后根据这个child node的类型,放到该类型对应链表里。

SampPackage里为每一种类型的node都维护了一个链表,比如所有的PostProccessor都放到一个List<PostProcessor> 链表里。

注意遍历TreePath时的顺序是从根节点到Sample节点,这个顺序是有关系的,这样能保证最下层的节点会放到链表里靠后的位置。

在执行链表里的节点时,下层节点会后执行,能够覆盖前面的值。

如何对变量(Variable)求值JMeter支持变量。

支持变量是一个非常重要的功能。

这样你可以使得你的Test Script(这里是JMeter TestPlan) 更有弹性。

适应更多测试场景。

JMeter支持很多种设置变量的方法,下面是其中一种,通过用户自定义的变量控件(User Defined Variables)。

在执行TestCase的控件里,可有对变量引用。

变量在运行时被求值。

在这一节里,我们要弄明白,到底一个变量时如何在运行时被求值的。

TestElement, Property和JMX在了解变量之前,先了解TestElement和Property。

在TestTree上的所有节点都是实现了TestElement接口。

TestElement可以看作是一个对象实例,而Property是对象属性。

每一个在TestTree上的节点,通过Property来定义其自身的属性。

当你保存一个TestPlan的时候,该TestTree上所有TestElement的所有Property 都会被持久化在JMX文件里,如下图所示。

Property的求值在定义Property的值的时候,你可以指定简单的值,比如“abc”,“223”,还可以指定变量,比如 "${ Server }"。

在执行TestPlan的时候,每当取得Property 的值时,Property的“字面值”会被“求值”,比如 "abc" 的值还是 "abc", 但是如果是变量,比如"${Server}",其值是运行时决定的。

那么变量${Server}是如何求值的呢?对变量求值,本质上就是在一个Map里根据变量名,查询变量值的过程。

在这里也不例外。

在JMeter里,每一个执行Test的Thread在执行时有一个Context,它是类JMeterContext 的实例。

JMeterContext维护了一个JMeterVariables实例。

JMeterVariables里面封装了一个Map。

这个Map就是变量查询值的地方。

需要注意的是JMeterVariables是特定于每一个线程的。

也就是每一个线程都有自己的独立的JMeterVariables,也就是每一个线程都专享一个独立的变量求值Map。

Variable可见范围在设置变量的值的时候,需要考虑的问题是“该值的可见范围”。

Variable Scope:在Testplan范围这些变量在testplan开始执行的时候初始化,然后就一直保持不变。

也就是是说它比较适合设置全局的变量。

使用TestPlan节点本身提供的机制代码在PreCompiler.addNode里也可以使用独立的Config Element:User Defined Variables。

代码同样在PreCompiler.addNode里Variable Scope:在Virtual User范围性能测试中的Virtual User在JMeter中体现为ThreadGroup里一个Thread。

那么在JMeter 里如何为每一个Thread设置变量呢?最简单的方法是使用PreProcessor:User Parameter。

在这个例子里,第一个Thread对Server求值会得到“192.168.1.1”,而第二个会得到“192.168.1.2”。

注意:很多用来设置变量的Configure元素带有开关使得其是否在Virtual User范围可见Variable Scope:在Loop范围在JMeter里,你也可以控制每一个Loop的变量的值。

如何实现Property里变量值的替换?对于JMeter的用户来说,在配置Sample的时候,对于一个Property设置的值像这样Server=${MyServer}, 那么到底JMeter是如何对${MyServer}求值的呢?在一次Test开始的时候,首先StandardJMeterEngine会得到执行权,在StandardJMeterEngine.run函数里。

PreCompiler会对TestTree进行处理,注意此时还没有启动ThreadGroup里的Thread。

PreCompiler把所有的TestElement定义的Property的值的字面值,比如"abc", "${MyServer}", 替换为函数ReplaceStringWithFunction的一个实例。

这样实现了延时求值。

下面展示了替换的过程PreCompiler.addNodeValueReplacer.replaceValuesReplaceStringWithFunction.transformValue在求值时, ReplaceStringWithFunction会调用其masterFunction来求值, 在函数里而ReplaceStringWithFunction的masterFunction是CompoundVariableCompoundVariable.execute在CompoundVariable.execute函数里对变量${MyServer}进行求值. 变量在这里是一个SimpleVariable实例。

SimpleVariable.toString在SimpleVariable.toString里,从JMeterContext.Variables里查找值。

最后一个问题,在CompoundVariable里字符串"${MyServer}"是如何变成SimpleVariable的呢。

相关主题