写点什么

分布式性能测试框架用例方案设想(二)

用户头像
FunTester
关注
发布于: 31 分钟前

书接上文分布式性能测试框架用例方案设想(一),方案二进行更加复杂的测试用例,以jar包类方法形式的用例进行测试,下期会基于docker进行技术验证。

基于 jar 包类方法的压测场景

性能测试脚本基于FunTester性能测试框架,今天分享提前将用例写好,暴露部分参数的用例。这类性能测试用例的测试场景已经提前设定,参数化目前都只是在测试线程数,软启动时间,终止条件等做了区分等等。


这类用例的修改增添都需要重新部署服务,所以比较适合稳定用例,比如基准性能测试用例这些,可用于性能巡检、定期性能回归等等测试场景。这种用例形式不同于用例方案设想(一)中的单请求和多请求模式,脚本的话已经可以支持更多的用例场景,包括单链路多链路全链路测试。对于每次请求都需要签名的接口也是非常不错的选择。


PS:彻底放弃了本地执行shell命令的方式,太复杂,不可控的东西比较多。

实现 Demo

这里我自己写了一个测试类,实现了无参方法基础类型参数方法String 对象类型参数String 数组类型参数四种方法的反射执行的Demo,功能基于com.funtester.frame.execute.ExecuteSource类,这个主要功能就是执行jar包内的方法,这个类的代码我会放在最后,大家也可以点击阅读原文查看仓库中的最新代码。


下面是测试类的代码:


public class Test3 extends SourceCode {
private static final Logger logger = LogManager.getLogger(Test3.class);
public static void main(String[] args) { ExecuteSource.executeMethod("com.funtest.javatest.Test3.funtest"); ExecuteSource.executeMethod("com.funtest.javatest.Test3.funtest", "java.lang.Integer", "1"); ExecuteSource.executeMethod("com.funtest.javatest.Test3.funtester", "java.lang.Integer", "1"); ExecuteSource.executeMethod("com.funtest.javatest.Test3.funtester", "java.lang.String", "33,3234,43,24,23,4,22");
}

public static void funtest() { output(Time.getDate()); }

public static void funtest(Integer i) { output(i + TAB + Time.getDate()); }
public static void funtester(int i) { output(i * 10 + TAB + Time.getDate()); }
public static void funtester(String[] i) { output(i.length * 10 + TAB + Time.getDate()); }
public static void funtester(String i) { funtester(i.split(COMMA)); }

}
复制代码


控制台输出:


INFO-> 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16INFO-> 2021-05-13 18:24:05INFO-> 1  2021-05-13 18:24:05WARN-> 方法属性处理错误!INFO-> 10  2021-05-13 18:24:05INFO-> 70  2021-05-13 18:24:05
Process finished with exit code 0
复制代码


其中这个报错,在之前的文章中提到过有兴趣大家可以参考:反射执行Groovy类方法NoSuchMethodException解答,当然直接忽略也是可以的。


这里有个坑,Java反射调用某个方法时,对于引入了基础类型(如int)参数的重载方法识别不准,有时候能找到,有时候找不到,我都怀疑是随机的。但是改成java.lang.Integer参数类型就完全没问题,这一点以后写用例的时候需要多多注意。


这里还有个坑,Java反射执行方法参数是String[]的时候,始终无法传参成功,我试了好几种方式,现在果断放弃了。因为我的用例都是写在Groovy类中的main方法中。所以将来需要对jar包内的所有用例添加一个方法:



/** * @param args */ public static void main(String args) { main(args.split(COMMA)); }
复制代码

用例创建

单接口

对于简单的用例的话,标准的用例编写格式如下:


public static void main(String[] args) {        ClientManage.init(60, 60, 0, "", 0);        ArgsUtil util = new ArgsUtil(args);        int thread = util.getIntOrdefault(0, 2);        int times = util.getIntOrdefault(1, 10);        Constant.RUNUP_TIME = util.getIntOrdefault(2, 10);        MySqlTest.LOG_KEY = false;        String url = "http://localhost:12345/test/qps";        JSONObject params = new JSONObject();        params.put("name", "FunTester");        params.put("password", "123456798");        Sign.sign(params);        HttpGet httpGet = getHttpGet(url,params);        HttpGet httpGet = getHttpGet(url);        RequestThreadTimes requestThreadTimes = new RequestThreadTimes(httpGet, times, null);        Concurrent funTester = new Concurrent(requestThreadTimes, thread, "FunTester测试反射执行");        funTester.start();    }
复制代码


依然采用com.funtester.frame.thread.RequestThreadTimes多线程任务类对象作为演示,而且是Java的,不过这个类已经被我标记过期了,我重新写了一个多线程对象,更加适合现在公司的项目,并且兼容了com.funtester.frame.thread.RequestThreadTimescom.funtester.base.constaint.ThreadLimitTimeCount类的功能。

多接口和链路测试

这两个区分度不大,需要额外单独实现被测方法,大家可以看看单链路性能测试实践文章中的链路设计和实现思路。这里只分享一下脚本内容,如下:



import com.alibaba.fastjson.JSONimport com.alibaba.fastjson.JSONObjectimport com.funtester.base.bean.AbstractBeanimport com.funtester.base.constaint.ThreadLimitTimesCountimport com.funtester.frame.execute.Concurrentimport com.funtester.httpclient.ClientManageimport com.funtester.utils.ArgsUtilimport com.okayqa.composer.base.OkayBaseimport com.okayqa.composer.function.Mirroimport com.okayqa.composer.function.OKClass
class Login_collect_uncollect extends OkayBase {
public static void main(String[] args) { ClientManage.init(10, 5, 0, "", 0) def util = new ArgsUtil(args) def thread = util.getIntOrdefault(0, 30) def times = util.getIntOrdefault(1, 40)
def tasks = []
thread.times { tasks << new FunTester(it, times) }
new Concurrent(tasks, "资源库1.4登录>查询>收藏>取消收藏链路压测").start()
allOver() }
private static class FunTester extends ThreadLimitTimesCount<Integer> {
OkayBase base
def mirro
def clazz
FunTester(Integer integer, int times) { super(integer, times, null) }
@Override void before() { super.before() base = getBase(t) mirro = new Mirro(base) clazz = new OKClass(base) }
@Override protected void doing() throws Exception {
def klist = mirro.getKList() def karray = klist.getJSONArray("data") K ks karray.each { JSONObject parse = JSON.parse(JSON.toJSONString(it)) if (ks == null) { def level = parse.getIntValue("node_level") def type = parse.getIntValue("ktype") def id = parse.getIntValue("id") ks = new K(id, type, level) } } JSONObject response = clazz.recommend(ks.id, ks.type, ks.level) def minis = [] int i = 0 response.getJSONArray("data").each { if (i++ < 2) { JSONObject parse = JSON.parse(JSON.toJSONString(it)) int value = parse.getIntValue("minicourse_id") minis << value } } clazz.unCollect(random(minis))
mirro.getMiniCourseListV3(ks.id, ks.type, 0, ks.level) } }
private static class K extends AbstractBean {
int id
int type
int level
K(int id, int type, int level) { this.id = id this.type = type this.level = level } }
}
复制代码

用例传输

上传用例

这个其实没啥可说的,就是写好用例,打包编译,然后重启服务即可。


多说一嘴,传说中的Java热更新和ClassLoader加载外部类和 jar 文件等高端技术,各位可以自行选择探索。

分配用例

采取和用例方案设想(一)中相同的方式,应该所有的设想中,分配用例思路都是一样的,不同的就是任务类对象的设计和实现。

用例执行

如通本文开始实现Demo中所写,就是执行测试任务中具体用例的方法了。执行类的代码如下:


package com.funtester.frame.execute;
import com.alibaba.fastjson.JSON;import com.funtester.base.exception.FailException;import com.funtester.frame.SourceCode;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Arrays;import java.util.List;
public class ExecuteSource extends SourceCode {
private static Logger logger = LogManager.getLogger(ExecuteSource.class);
/** * 执行方法 * <p>防止编译报错,用list绕一圈</p> * * @param params */ public static Object executeMethod(List<String> params) { Object[] objects = params.subList(1, params.size()).toArray(); return executeMethod(params.get(0), objects); }
/** * 执行方法 * <p>防止编译报错,用list绕一圈</p> * * @param params */ public static Object executeMethod(String[] params) { return executeMethod(Arrays.asList(params)); }
/** * 执行具体的某一个方法,提供内部方法调用 * <p>重载方法如果参数是基础数据类型会报错</p> * * @param path * @param paramsTpey */ public static Object executeMethod(String path, Object... paramsTpey) { int length = paramsTpey.length; if (length % 2 == 1) FailException.fail("参数个数错误,应该是偶数"); String className = path.substring(0, path.lastIndexOf(".")); String methodname = path.substring(className.length() + 1); Class<?> c = null; Object object = null; try { c = Class.forName(className); object = c.newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { logger.warn("创建实例对象时错误:{}", className, e); } Method[] methods = c.getDeclaredMethods(); for (Method method : methods) { if (!method.getName().equalsIgnoreCase(methodname)) continue; try { Class[] classs = new Class[length / 2]; for (int i = 0; i < paramsTpey.length; i +=2) { classs[i / 2] = Class.forName(paramsTpey[i].toString());//此处基础数据类型的参数会导致报错,但不影响下面的调用 } method = c.getMethod(method.getName(), classs); } catch (NoSuchMethodException | ClassNotFoundException e) { logger.warn("方法属性处理错误!"); } try { Object[] ps = new Object[length / 2]; for (int i = 1; i < paramsTpey.length; i +=2) { String name = paramsTpey[i - 1].toString(); String param = paramsTpey[i].toString(); Object p = param; if (name.contains("Integer")) { p = Integer.parseInt(param); } else if (name.contains("JSON")) { p = JSON.parseObject(param); } ps[i / 2] = p; } method.invoke(object, ps); } catch (IllegalAccessException | InvocationTargetException e) { logger.warn("反射执行方法失败:{}", path, e); } break; } return null; } }
复制代码


有一些暂时无用的方法我已删除,有兴趣的可以去仓库看看。

Have Fun ~ Tester !

FunTester,一群有趣的灵魂,腾讯云 &Boss 认证作者,GDevOps 官方合作媒体。




点击阅读阅文,查看 FunTester 历史原创集合

发布于: 31 分钟前阅读数: 2
用户头像

FunTester

关注

公众号:FunTester,Have Fun, Tester! 2020.10.20 加入

Have Fun,Tester!

评论

发布
暂无评论
分布式性能测试框架用例方案设想(二)