背景
最近一段时间在接触性能压测,遇到一个棘手的问题。性能需求在30KQPS,要求进行单接口压测,接口之间依赖不可避免(下一个接口发压数据需要使用上一接口的返回),还不能通过做数据的方式准备。只能将上一接口返回的数据,保存起来,用于下一接口的参数。
在一开始的时候,犯了一个很二的错误,将数据写入到Jmeter的日志中,再进行提取(发压端文件IO影响性能不是一点点),然后将受影响的性能指标作为测试结果(可进行两次测试,第一次,不写日志,性能指标作为测试结果,第二次,写日志,采集的数据作为下一接口的配置元)。
当然了,这样的工作,为什么要每次都做两遍呢,当然可以考虑开发一个插件来做了。首先考虑的就是写内存记录下来。
主要考虑两个方向:
① Jmeter内存
② Redis
其中,Jmeter内存保存,仅在本次测试有效,还需要在测试结束时,将内存中数据,保存到文件。考虑用redis,网上搜索了一把,jmeter-plugins-redis能满足读取redis数据的需求(不重复造轮子,就用它了),只要自己完成一个写入数据到redis的插件就可以了。
准备工作
下载Redis插件
官方提供的网址:https://jmeter-plugins.org/wiki/RedisDataSet/
下载下来之后,继续下载依赖的jedis版本,官方依赖的是jedis 2.2.1
然鹅,在jmeter 3.0版本下,竟然用不起来,抛出了一票的异常。度娘了一把(请原谅,公司翻墙要自备梯子),是jedis的版本过低,jedis 2.4.1 以上解决了。正好,项目组其他插件依赖2.8.1,索性就用这个版本。
源码修改
用Jedis 2.8.1后,又抛异常,真是沮丧啊。怎么办?思来想去,改源码,就不信这个邪!
github上一搜索,还真就有。二话不说,下载源码:https://github.com/undera/jmeter-plugins
这位老哥undera,还是很厚道的,插件源码都来了。
打开源码,进行编译,提示下面3个函数已经没有了:
那就看下怎么修改吧,setMaxActive -> setMaxTotal,setMaxWait -> setMaxWaitMillis,最后一个实在没有找到,先注释掉。
编译,生成jar包,放进去跑一把,界面出来了。读取数据,就算搞定了,下面去开发写数据。
二次开发
由于前面已经下载了源码,而且jmeter插件网站上,提供了源码,怎么配合Redis Data Set的get mode,插入数据。
那就先做简单的,证明这种方式可以用。考虑用后置处理器来解决。
简单应用
1、添加数据操作类:RedisDataWriter
2、添加界面显示:RedisDataWriterGUI
想法很简单,先用起来,能在Jmeter上,找到这个后置处理器,能将数据插入到redis里面。连接信息之类的,都先给写死。
跑完第一把,发现数据竟然能写到redis,很满足了。
添加界面
有了上面的经验,那就可以慢慢的往上添加界面,优化连接。
RedisDataWriterGUI.java
import com.bilibili.redis.RedisDataWriter;
import org.apache.jmeter.processor.gui.AbstractPostProcessorGui;
import org.apache.jmeter.testelement.TestElement;
import java.awt.*;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JPanel;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.gui.JLabeledField;
import org.apache.jorphan.gui.JLabeledTextField;
import org.apache.jorphan.gui.JLabeledChoice;
public class RedisDataWriterGUI extends AbstractPostProcessorGui{
private JLabeledTextField redisKeyField;
private JLabeledTextField variableNamesField;
private JLabeledChoice addModeField;
private JLabeledTextField redisServerHostField;
private JLabeledTextField redisServerPortField;
private JLabeledTextField redisServerTimeoutField;
private JLabeledTextField redisServerPasswdField;
private JLabeledTextField redisServerDbField;
private JLabeledTextField redisPoolMinIdleField;
private JLabeledTextField redisPoolMaxIdleField;
private JLabeledTextField redisPoolMaxActiveField;
private JLabeledTextField redisPoolMaxWaitField;
public RedisDataWriterGUI() {
super();
init();
}
@Override
public void configure(TestElement el) {
super.configure(el);
if (el instanceof RedisDataWriter){
RedisDataWriter re = (RedisDataWriter) el;
redisKeyField.setText(re.getRedisKey());
variableNamesField.setText(re.getVariableNames());
addModeField.setText(re.getRedisAddMode());
redisServerHostField.setText(re.getRedisHost());
redisServerPortField.setText(re.getRedisPort());
redisServerTimeoutField.setText(re.getRedisTimeout());
redisServerPasswdField.setText(re.getRedisPasswd());
redisServerDbField.setText(re.getRedisDatabase());
redisPoolMinIdleField.setText(Integer.toSt