通过一阵子的断断续续的测试,DCS_FunTester分布式压测框架更新(一)完毕以后又增长了一些更新。java
在分布式性能测试框架用例方案设想(三)、基于docker的分布式性能测试框架功能验证(三)中,我提到了方案三:基于Groovy
脚本执行的测试用例,此次更新将支持执行Groovy
测试用例。目前除了访问验证之外,可是对于脚本内容还没有过滤。docker
下面是master
节点实现方法:markdown
@Override
int runScript(GroovyScript script) {
def mark = SourceCode.getMark()
def num = script.getMark()
def hosts = NodeData.getRunHost(num)
try {
hosts.each {
script.setMark(mark)
def re = MasterManager.runRequest(it, script)
if (!re) FailException.fail()
NodeData.addTask(it, mark)
}
} catch (FailException e) {
hosts.each { f -> MasterManager.stop(f) }
FailException.fail("多节点执行失败!")
}
mark
}
复制代码
下面是slave
节点的实现方法:架构
@Override
public void runScript(GroovyScript script) {
ExecuteGroovy.executeScript(script.getScript());
}
复制代码
这里没有传值,留个参数params
之后能够用来作脚本化参数配置。框架
增长了master
节点以后也就没有slave
节点的直接访问。dom
注册机制我本身写了一个简单的实现,放在一个类里面。异步
package com.funtester.master.common.basedata;
import com.funtester.base.bean.PerformanceResultBean;
import com.funtester.base.exception.FailException;
import com.funtester.frame.SourceCode;
import com.funtester.master.common.bean.manager.RunInfoBean;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class NodeData {
/** * 节点状态 */
public static ConcurrentHashMap<String, Boolean> status = new ConcurrentHashMap<>();
/** * 节点的运行信息,经过progress获取 */
public static ConcurrentHashMap<String, String> runInfos = new ConcurrentHashMap<>();
/** * 节点的运行结果 */
public static ConcurrentHashMap<Integer, List<PerformanceResultBean>> results = new ConcurrentHashMap<>();
/** * 节点更新时间 */
public static ConcurrentHashMap<String, Integer> time = new ConcurrentHashMap<>();
/** * 节点运行的任务id */
public static ConcurrentHashMap<String, Integer> tasks = new ConcurrentHashMap<>();
public static void register(String host, boolean s) {
synchronized (status) {
status.put(host, s);
mark(host);
}
}
/** * 可用节点 * * @return */
public static List<String> available() {
synchronized (status) {
List<String> availables = new ArrayList<>();
status.forEach((k, v) -> {
if (v) availables.add(k);
});
return availables;
}
}
/** * 标记节点时间 * * @param host */
private static void mark(String host) {
time.put(host, SourceCode.getMark());
}
/** * 检查,删除过时节点和过时数据,提供定时任务执行 */
public static void check() {
int timeStamp = SourceCode.getMark();
List<String> hkeys = new ArrayList<>();
synchronized (status) {
time.forEach((k, v) -> {
if (timeStamp - v > 12) {
hkeys.add(k);
}
});
hkeys.forEach(f -> status.remove(f));
}
synchronized (runInfos) {
hkeys.forEach(f -> runInfos.remove(f));
}
synchronized (tasks) {
hkeys.forEach(f -> tasks.remove(f));
tasks.forEach((k, v) -> {
if (timeStamp - v > 60 * 30) tasks.put(k, 0);
});
}
synchronized (results) {
List<Integer> tkeys = new ArrayList<>();
results.forEach((k, v) -> {
if (k - timeStamp > 3_3600) {
tkeys.add(k);
}
});
tkeys.forEach(f -> results.remove(f));
}
}
/** * 添加运行信息 * * @param bean */
public static void addRunInfo(RunInfoBean bean) {
synchronized (runInfos) {
runInfos.put(bean.getHost(), bean.getRuninfo());
}
}
/** * 获取描述的的用例任务运行信息 * * @param desc 任务描述信息 * @return */
public static List<String> getRunInfo(String desc) {
synchronized (runInfos) {
ArrayList<String> infos = new ArrayList<>();
runInfos.forEach((k, v) -> {
if (v.contains(desc)) {
infos.add(v);
}
});
return infos;
}
}
/** * 添加运行信息 * * @param bean */
public static void addResult(int mark, PerformanceResultBean bean) {
synchronized (results) {
results.computeIfAbsent(mark, f -> new ArrayList<PerformanceResultBean>());
results.get(mark).add(bean);
}
}
/** * 添加节点运行任务id * @param host * @param mark */
public static void addTask(String host, Integer mark) {
synchronized (tasks) {
if (status.get(host) != null && status.get(host) == false) {
tasks.put(host, mark);
}
}
}
public static List<String> getRunHost(int num) {
synchronized (status) {
List<String> available = available();
if (num < 1 || num > available.size())
FailException.fail("没有足够节点执行任务");
List<String> nods = new ArrayList<>();
for (int i = 0; i < num; i++) {
String random = SourceCode.random(available);
status.put(random, false);
nods.add(random);
}
return nods;
}
}
}
复制代码
这里写的有点复杂,将来计划写入Redis
或者借助其余的成熟组件完成。原本想把节点信息封装成一个对象的形式,后来想一想仍是比较麻烦,若是分开处理会比较容易。分布式
统一由master
节点分配任务运行用例,天然要取消slave
节点的访问权限,可是如今还有一部分接口暴露出来,swagger
文档中没有表名。其实刷新master节点信息
和从新注册节点
两个功能留做子节点出错时候使用。ide
以前的功能全然写成了一个静态方法,提取了service
接口,主要方法以下:oop
package com.funtester.master.service
import com.funtester.slave.common.bean.run.GroovyScript
import com.funtester.slave.common.bean.run.HttpRequest
import com.funtester.slave.common.bean.run.HttpRequests
import com.funtester.slave.common.bean.run.LocalMethod
import com.funtester.slave.common.bean.run.ManyRequest
interface IRunService {
public int runRequest(HttpRequest request) public int runRequests(HttpRequests request) public int runMethod(LocalMethod method) public int runScript(GroovyScript script) } 复制代码
其中每一个对象都有一个mark
属性,对于master
节点来讲,就是执行的节点数,对于slave
节点来讲,就是执行任务的标记。
这里分享一下思路:
master
节点slave
节点,首先会请求master
(配置或者接口设置),获取本机IP
slave
节点经过定时任务将状态同步到master
节点。没有使用Socket
接口,总以为麻烦。
Have Fun ~ Tester !
点击阅读阅文,查看FunTester历史原创集合