DCS_FunTester分佈式壓測框架更新(二)

通過一陣子的斷斷續續的測試,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或者藉助其餘的成熟組件完成。原本想把節點信息封裝成一個對象的形式,後來想一想仍是比較麻煩,若是分開處理會比較容易。分佈式

取消slave節點訪問

統一由master節點分配任務運行用例,天然要取消slave節點的訪問權限,可是如今還有一部分接口暴露出來,swagger文檔中沒有表名。其實刷新master節點信息從新註冊節點兩個功能留做子節點出錯時候使用。ide

service層提取

以前的功能全然寫成了一個靜態方法,提取了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節點來講,就是執行任務的標記。

更新同步信息

這裏分享一下思路:

  1. 啓動master節點
  2. 啓動slave節點,首先會請求master(配置或者接口設置),獲取本機IP
  3. 而後slave節點經過定時任務將狀態同步到master節點。

沒有使用Socket接口,總以爲麻煩。

Have Fun ~ Tester !

FunTester測試框架和分佈式測試框架DCS_FunTester官方帳號,歡迎關注!


點擊閱讀閱文,查看FunTester歷史原創集合

相關文章
相關標籤/搜索