Jmeter 提供了一组函数可以帮我们在做性能测试的时候更好的做参数化,比如字符编码格式转换、读取运行时变量值等等。但是有时候我们需要根据业务进行自定义的函数开发。
比如最近一个项目,在发送 http 请求的时候,客户端需要发 Token,它是根据给项目分配的 ID 和 Key 值以及系统当前时间生成的,服务端会验证该 Token 的有效性。
开发自定义函数,网上一般有两种做法:
第一种方法既复杂,又不易于维护管理,我个人喜欢用第二种方法。
第二种方法的大坑就是 package 命名有特殊的要求:package 路径中必须包含 functions 一级。
我用 maven 管理项目,因此只需要在 pom 文件中加入以下引用。
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_core</artifactId>
<version>3.0</version>
</dependency>
注意:路径中必须包含 functions,否则 jmeter 无法识别。JMeter 设计让一些核心的类(非 UI 相关的,比如 ApacheJMeter_core 等)可以在非 UI 的方式下运行的时候能被加载进来,这些类会被优先加载。加载这些类的时候是通过命名规则来实现的。所有实现 function 的类必需包含".functions."。
当然也可以通过更改 jmeter.properties 中的配置来实现改变命名规则,如下所示。但是一般来说不推荐更改此项配置。
classfinder.functions.contain=.functions.
这四个方法介绍如下:
public String execute(SampleResult previousResult, Sampler currentSampler) throws InvalidVariableException
JMeter 会将上次运行的 SampleResult 和当前的 Sampler 作为参数传入到该方法里,返回值就是在运行该 function 后得到的值,以 String 类型返回。该方法如果操作了非线程安全的对象(比如文件),则需要将对该方法进行线程同步保护。
public void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException;
这个方法在用于传递用户在执行过程当中传入的实际参数值。该方法在 function 没有参数情况下也会被调用。一般该方法传入的参数会被保存在类内全局变量里,并被后面调用的 execute 方法中使用到。
public String getReferenceKey();
这个就是 function 的名字。JMeter 的命名规则是在方法名前面加入双下划线"__"。比如"__GetEven",function 的名字跟实现该类的类名应该一致,而且该名字应该以 static final 的方式在实现类中定义好,避免在运行的时候更改它。
public List<String> getArgumentDesc();
最后在你的实现类中还需要提供一个方法来告诉 JMeter 关于你实现的 function 的描述。
我自己编写的 GetToken 类如下:
package stressTest.functions;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.functions.AbstractFunction;
import org.apache.jmeter.functions.InvalidVariableException;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.util.encoders.Hex;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
* Created by pengshenshen on 2018/3/12.
*/
public class GetToken extends AbstractFunction {
private static final List<String> desc = new LinkedList<String>();
private static final String KEY = "__getToken";
private CompoundVariable[] values;
// Number of parameters expected - used to reject invalid calls
private static final int MIN_PARAMETER_COUNT = 2;
private static final int MAX_PARAMETER_COUNT = 3;
static {
desc.add(JMeterUtils.getResString("appId"));
desc.add(JMeterUtils.getResString("apiKey"));
}
@Override
public String execute(SampleResult sampleResult, Sampler sampler) throws InvalidVariableException {
String appId = values[0].execute();
String apiKey = values[1].execute();
long currentTimeMillis = System.currentTimeMillis();
// 5 minutes
currentTimeMillis = currentTimeMillis - (currentTimeMillis % (1000 * 60 * 5));
byte[] data = (appId + apiKey + currentTimeMillis).getBytes();
MD5Digest md5 = new MD5Digest();
md5.update(data, 0, data.length);
byte[] digest = new byte[md5.getDigestSize()];
md5.doFinal(digest, 0);
return Hex.toHexString(digest);
}
@Override
public void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException {
checkParameterCount(parameters, MIN_PARAMETER_COUNT, MAX_PARAMETER_COUNT);
values = parameters.toArray(new CompoundVariable[parameters.size()]);
}
@Override
public String getReferenceKey() {
return KEY;
}
@Override
public List<String> getArgumentDesc() {
return desc;
}
}
我用 maven-assembly-plugin 打包。方法如下:
首先在 pom 文件中引入插件:
<plugin>
<artifactId>maven-assembly-plugin</artifactId><configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
然后执行打包命令:mvn clean compile assembly:single
将打出来的 jar 包拷贝至 jmeter 的目录:Jmerter_HOME\lib\ext
Jmerter_HOME\bin\jmeter.bat,在函数助手界面中可以找到我的函数:
随便做一个 post 请求,测试该方法是否有效
向百度发一个 post 请求,查看一下 body 里面传 getToken 函数的结果
如图:在查看结果树中看到:请求的 body 中已经带了 Token 值。
以上函数介绍部分参照:https://www.jianshu.com/p/a88e5cb1d6cb (这个作者有产出不少 jmeter 的文章,在此标明出处,以后也可以慢慢看)