通過一陣子的斷斷續續的測試,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歷史原創集合