Jmeter---自定义函数function
Jmeter对比LoadRunner,在场景设置上的不同,包括但不限于:
- LoadRunner以脚本被分配的用户数,设置事务占总场景的百分比。
- Jmeter以线程数的大小,设置事务占总场景的百分比。
- Jmeter很好的支持同一线程组下使用不同协议的请求。
- LoadRunner需要以多协议脚本的方法实现不同协议的请求。
对于平常性能测试,这些不同点不会造成太大的问题。
但对于特定需求来说,以上两种工具使用都不太方便,比如:
- 若每个线程组下拥有一个事务,因每个线程组至少分配一个线程,所以每个事务的占比>=1/总线程数。有时需要比这更小的占比。
- 若给定并发数(线程数),可能出现部分事务无法分配占比的情况。
- 当事务数多达30以上时,维护成本较高。
综上两点,我们需要一个测试工具,它可以实现:
- 可对场景指定任意并发数。
- 可精准控制事务占比。
- 可快捷维护上述两点。
思路构想:
- 使用文件维护各事务占比。
- 以上述文件占比总和为上限,使用均匀分布的概率,生成随机数。
- 根据生成的随机数,选取对应的事务,发起请求。
Jmeter初期实现:
- 使用Java Properties文件维护占比
//out.Properties
trans1=10.00
trnas2=20.00
trnas3=40.50
trnas4=29.50
- 使用Java Request实现均匀分布选取。
- Jmeter界面长这样
上述实现初步实现我们的思路,但存在以下缺点:
- Jmeter报告统计及原始的jtl结果收集文件里,均包含Java Request事务,不便于获取场景的总TPS。
- out.Properties文件仍以百分比维护,一是精确度不高;二是需要做额外的工作,将统计/预测出来的交易数划算为百分比的形式。
Jmeter改进实现:
- 以Function的形式代替Java Request,规避多余的请求数。
- 更改out.Properties文件,以整数大小形式说明各事务占比。
更改后界面长这样:
out.Properties可直接维护交易数或占比:
//out.Properties
trans1=1005
trnas2=2009
trnas3=4050
trnas4=2950
自定义的函数实现:
这里有小小的碎碎念,鄱Jmeter源码时,走错了方向,作了很多无用功,却熟悉了Jmeter nonGUI&nonRemote模式下基本的工作机制,心疼下自己0.0
函数功能说明:
- 参数1:指定场景文件,即Properties文件的路径。
- 参数2:boolean值,可选,当为true时,每次调用时使用新的随机数种子。
- 参数3: 函数返回结果赋于新的变量名,可选。
- 备注:当该函数第一次调用时,函数执行初始化(1.读取Properties;2.映射事务名与随机数的关系。)
映射关系:
trans1 | trans2 | trans3 | trans4 |
---|---|---|---|
0~1005 | 1006~3014 | 3015~7064 | 7065~10014 |
备注:3014 = trans2+trans1 = 1005+1006。7064和10014类推。
Java代码实现Function
package org.apache.jmeter.functions;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.util.JMeterUtils;
import java.util.TreeMap;
import java.util.Properties;
import java.io.FileNotFoundException;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Set;
import java.util.Map;
import java.time.Instant;
import java.util.Random;
public class TransactionSwitch extends AbstractFunction{
private static final List<String> desc = new LinkedList<>();
private static final String KEY = "__TransactionSwitch"; //$NON-NLS-1$
static {
desc.add("PropertiesFileName");
desc.add("hasRandomSeedVariation(optional)");
desc.add(JMeterUtils.getResString("function_name_paropt"));
}
private Object[] values;
private Random random = new Random();
private static int bound ;
private static TreeMap<Integer,String> map = new TreeMap<>((x,y)->( x==y?0:(x>y?1:0)));
private static Boolean firstCall = true;
/**
* No-arg constructor.
*/
public TransactionSwitch() {
firstCall = true;
map.clear();
}
/** {@inheritDoc} */
@Override
public String execute(SampleResult previousResult, Sampler currentSampler)
throws InvalidVariableException {
if (firstCall){
init(((CompoundVariable)values[0]).execute().trim());
}
if(Boolean.valueOf(((CompoundVariable)values[1]).execute().trim())) {
random.setSeed(Instant.now().toEpochMilli());
}
int nextInt = random.nextInt(bound);
Map.Entry<Integer,String> entry = map.entrySet().stream().filter(s->
s.getKey() >= nextInt
).limit(1).findFirst().get();
return entry.getValue();
}
private synchronized void init(String configFile){
Properties prop = new Properties();
try (var in = new FileInputStream(configFile)){
prop.load(in);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
bound = 0;
Set<String> set = prop.stringPropertyNames();
set.stream().distinct().forEach(s -> {
int a = Integer.parseInt(prop.getProperty(s).trim());
bound += a;
map.put(bound, s.trim());
});
bound++;
firstCall = false;
}
/** {@inheritDoc} */
@Override
public void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException {
checkMinParameterCount(parameters,2);
values = parameters.toArray();
}
/** {@inheritDoc} */
@Override
public String getReferenceKey() {
return KEY;
}
/** {@inheritDoc} */
@Override
public List<String> getArgumentDesc() {
return desc;
}
}
编译并打包上述代码,放于JMETER_HOME/lib/ext目录下,重启jmeter,可以Jmeter的function helper看到:
事后验证
- 经测试数据分析与验证,调用该函数不影响负载机性能。
- 若某一事务占比较小(比如万分之几),运行2~3min即可覆盖到所在事务。
感谢阅读~

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
利用ApiPost一键、快速生成接口文档!女猿也过38节!
对于我们这些程序员和程序媛来讲,最头疼的莫过于写文档。 当女程序媛遇到问题,那就不是问题 我们可都是正个八经的理工校草和理工女神,研究github、逛逛csdn、写hello world是才我们的拿手菜,写文档是文科生的事情好不啦?(手动吐哇吐) 今天,教大家一个妙招:利用ApiPost一键、快速生成接口文档!妈妈再也不用担心自己女孩纸们没有时间过38节啦! ApiPost简介: ApiPost是一个支持团队协作,并可直接生成文档的API调试、管理工具。它支持模拟POST、GET、PUT等常见请求,是后台接口开发者或前端、接口测试人员不可多得的工具 。 官网: https://www.apipost.cn/ 您对ApiPost了解的不多,可以先看看这里: https://yq.aliyun.com/articles/692656?spm=a2c4e.11153940.blogcont692797.19.85ba723e3C9Kfx 利用ApiPost一键、快速生成接口文档: 当然,你得先下载安装apipost谷歌拓展或者客户端(个人建议客户端)。如图 接下来介绍具体步骤。 1、我们先准...
-
下一篇
NET Core 3.0-preview3 发布
NET Core 3.0-preview3 发布 .NET Core 3.0 Preview 3已经发布,框架和ASP.NET Core有许多有趣的更新。这是最重要的更新列表。 下载地址 :https://aka.ms/netcore3download。 .NET Core 3.0的更新: C#中对索引和范围的更多支持 支持.NET Standard 2.1。以.NET Standard项目文件为目标,并将netstandard2.1指定为目标框架。完整的.NET Framework不支持.NET Standard 2.1。 F#4.6和dotnet fsi命令。可以使用F#4.6和dotnet fsi命令的预览。FSI代表F#互动。 AssemblyDependencyResolver和resolver事件。从给定路径加载依赖程序集(之前不可能),解析程序事件可帮助我们更好地处理动态加载的本机依赖项。 Windows Forms应用程序的高DPI。最后,微软将Windows Forms应用程序推向了当今时代。96DPI不再适用,并且可以构建高DP Windows窗体应用程序。 A...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker容器配置,解决镜像无法拉取问题
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- 2048小游戏-低调大师作品
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- MySQL数据库在高并发下的优化方案
- Dcoker安装(在线仓库),最新的服务器搭配容器使用
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题