1. 程式人生 > 實用技巧 >jmeter壓力測試簡單使用

jmeter壓力測試簡單使用

結對專案

軟體工程 班級網址
作業要求 作業要求程式碼
作業目標 積累結對專案經驗,提高交流和合作能力

合作者

黃博曉 資訊保安 (1) 班 學號 : 3118005363

凌文宇 資訊保安 (1) 班 學號 : 3118005372

作業程式碼連結

github 網址 : https://github.com/bxxiao/bxxiao/tree/master/ArithmeticGeneratorProject

PSP 表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 30 40
· Estimate · 估計這個任務需要多少時間 30 40
Development 開發 740 790
· Analysis · 需求分析 (包括學習新技術) 200 150
· Design Spec · 生成設計文件 50 40
· Design Review · 設計複審 20 40
· Coding Standard · 程式碼規範 (為目前的開發制定合適的規範) 30 30
· Design · 具體設計 40 60
· Coding · 具體編碼 300 350
· Code Review · 程式碼複審 40 50
· Test · 測試(自我測試,修改程式碼,提交修改) 60 70
Reporting 報告 70 85
· Test Repor · 測試報告 30 30
· Size Measurement · 計算工作量 15 20
· Postmortem & Process Improvement Plan · 事後總結, 並提出過程改進計劃 25 35
· 合計 840 915

1. 設計實現過程

包結構:

  • expression包:一個Expression物件表示一個四則表示式,其中提供了隨機建立表示式、檢驗表示式、獲取表示式運算結果和返回表示式字串形式等方法。一個四則表示式封裝為一個二叉樹,TreeNode是二叉樹的結點,每個結點儲存一個運算子或運算數。

  • util包:

    • Calculator:主要提供了針對整數、分數的加減乘除運算、以及獲取分數的最簡形式的靜態方法。運算結果以字串形式返回。返回形式有整數和分數(如1/3,2'5/6)形式。

    • Ran:用於生成隨機值。包括隨機生成運算子、隨機生成運算數(整數和分數)以及隨機生成運算子在二叉樹中的位置。

    • CommandResolver:命令解析器。解析main方法的args引數,獲取其中的有用引數。若輸入形式不符合要求,會列印提示資訊。若輸入形式符合要求,則獲取其中指定的題目數和最大值並儲存。

    • FilteUtil:用於將生成的題目跟答案寫入檔案。寫入的格式如下:

      1. exp1 = or answer1
      2. exp2 = or answer2
      3. exp3 = or answer3
      ...
      
  • generator包:

    • Generator:提供了一個generate()方法,使用CommandResolver解析輸入的命令,獲取題目數和最大值,根據這兩個值建立表示式和對應的答案,並使用FilteUtil將題目和答案寫入當前目錄下的expFile.txt和answers.txt檔案(若不存在會建立)。在建立每個表示式之前,先隨機生成一個1-3的整數,作為運算子個數。
    • Main:包含main方法。
  • test包:包含一個測試類。(使用Junit)

2. 程式碼說明

2.1 表示式的字串形式

一個表示式對應的二叉樹是一個完全二叉樹,其中,葉子結點都是運算數,父結點都是運算子。對這樣的二叉樹進行中序遍歷可得到一個運算表示式,且每次返回父結點都會帶上括號,如:

遍歷結果是((1+2)-(3x4))

再通過以下原則去除多餘的括號

假設待去括號的表示式為(m1 op1 n1) op (m2 op2 n2)這裡m1、n1、m2、m2可能自身就是個表示式,也可能是數字,op、op1、op2為運算子

1、若op為'÷',則 (m2 op2 n2)的括號必須保留;

2、若op為'x'或'-',如果op2為'+'或'-',則(m2 op2 n2)的括號必須保留;

3、若op為'x'或'÷',如果op1為'+'或'-',則(m1 op1 n1)的括號必須保留;

4、 除此之外,去掉括號不影響表示式的計算順序。

如上面例子去掉多餘括號後變為:1+2-3x4

將當前二叉樹按以上規則轉換為字串形式的方法在TreeNode中:

public String toString(){
    String leftExp = "";//左表示式
    String rightExp = "";//右表示式
    String result = "";//運算子

    //若當前結點有孩子
    if(hasChild()){
        //右子樹如果有孩子,說明右子樹是一個表示式,而不是數字節點。
        if(getRchild().hasChild()){
            //判斷左鄰括號的運算子是否為'/'
            if(str.equals("÷")){
                //獲取右子樹的表示式,保留括號
                rightExp = getRchild().toString();
            }
            //判斷左鄰括號的運算子是否為'x'或'-'
            else if(str.equals("x") || str.equals("-")){
                //判斷op是否為'+'或'-'
                if(getRchild().str.equals("+") || getRchild().str.equals("-")){
                    rightExp = getRchild().toString();
                }
                else{
                    //獲取右子樹的表示式,並且去括號
                    rightExp = getRchild().toString().substring(1, getRchild().toString().length()-1);
                }
            }
            else{
                //右子樹除此之外都是可以去括號的。
                rightExp = getRchild().toString().substring(1, getRchild().toString().length()-1);
            }
        }
        else{
            rightExp = getRchild().str;
        }

        //左子樹的情況同右子樹類似
        if(getLchild().hasChild()){
            if(str.equals("x") || str.equals("÷")){
                if(getLchild().str.equals("+") || getLchild().str.equals("-")){
                    leftExp = getLchild().toString();
                }
                else{
                    leftExp = getLchild().toString().substring(1, getLchild().toString().length()-1);
                }
            }
            else{
                leftExp = getLchild().toString().substring(1, getLchild().toString().length()-1);
            }
        }
        else{
            leftExp = getLchild().str;
        }
        //獲取當前的運算式,並加上括號
        result = "(" + leftExp + " " + this.str + " " + rightExp + ")";
    }
    else{
        //若沒有孩子,說明是數字節點,直接返回數字
        result = this.str;
    }
    return result;
}

2.2 隨機生成表示式

2.2.1 隨機建立表示式 

隨機生成二叉樹的思路:

  • 先隨機生成一個運算子作為根結點。若只有一個運算子,直接生成兩個運算數作為根節點的左右孩子。
  • 若有2或3個運算子:
    • 先根據運算子個數隨機獲取運算子結點位置的boolean陣列(見下文getChildPlace())。
    • 遍歷該boolean陣列,若為true,則要建立一個表示式子樹(運算子為父結點,運算數為其左右孩子);若為false,則建立一個運算數子樹(只有一個結點)。
    • 在上述遍歷中設定一個遍歷索引i,若i為偶數,則建立的子樹作為"當前結點"的左孩子,否則為右孩子。且每建立一個作為左孩子的表示式子樹,則該子樹的根節點作為“當前結點”(當前結點從第一步生成的根節點開始)...
    • 這種方式建立子樹能保證最後生成的二叉樹是完全二叉樹。
  • 建立完表示式,呼叫CalAndVal()對錶達式校驗。
public void createExpression(){
    TreeNode lchild, rchild, lnode, rnode;

    //只有一個運算子
    if(opCounts == 1){
        lchild = new TreeNode(Ran.getNumber(max), null, null);
        rchild = new TreeNode(Ran.getNumber(max), null, null);
        root = new TreeNode(Ran.getOperator(), lchild, rchild);
    }
    else{
        int num1 = 0;
        boolean[] place = Ran.getChildPlace(opCounts);
        root = new TreeNode(Ran.getOperator(), null, null);
        opeList.add(root);

        for(int i = 0; i < place.length; i++){
            //place[i]為true,生成一個運算子,併為該運算子生成兩個左右孩子運算數
            if(place[i]){
                lnode  = new TreeNode(Ran.getNumber(max), null, null);
                rnode  = new TreeNode(Ran.getNumber(max), null, null);
                //i為偶數(從0開始的),將新建立的運算子作為當前結點(運算子)的左孩子;為奇,則作為右孩子
                if(i%2 == 0){
                    lchild = new TreeNode(Ran.getOperator(), lnode, rnode);
                    opeList.add(lchild);
                    opeList.get(num1).setLchild(lchild);
                }
                else{
                    rchild = new TreeNode(Ran.getOperator(), lnode, rnode);
                    opeList.add(rchild);
                    opeList.get(num1).setRchild(rchild);
                }
            }
            //place[i]為false,則只生成一個運算數,組裝到當前運算子的左右孩子
            else{
                if(i%2 == 0){
                    lchild = new TreeNode(Ran.getNumber(max), null, null);
                    opeList.get(num1).setLchild(lchild);
                }
                else{

                    rchild = new TreeNode(Ran.getNumber(max), null, null);
                    opeList.get(num1).setRchild(rchild);
                }
            }
            //num1為偶數,說明當前結點還未生成有孩子,num1不變;若是奇數,則+1
            num1 = num1 + i%2;
        }
    }
    CalAndVal();//生成完二叉樹進行校驗
}

2.2.2 隨機生成運算子結點位置

Ran類中包含一個根據運算子個數隨機生成運算子結點位置的方法,其實現思路:

  • 方法返回返回一個長度為2的boolean陣列,true元素的個數表示要新建立的運算子個數。
    • 在隨機生成表示式的方法中會使用到該方法,true表示在當前結點中,建立一個以運算子為父結點,兩個運算數為葉子結點的子二叉樹,作為當前結點的左或右孩子;false則表示建立一個運算數,作為當前結點的左或右孩子。
  • 若運算子個數為2,則需要再建立一個運算子,所以true,false各一個。
  • 運算子個數為3,則需要再建立兩個運算子。元素都是true。
public static boolean[] getChildPlace(int num){
    boolean[] result = new boolean[]{true, true};

    if(num==2){
        Random random = new Random();
        if(random.nextBoolean()){
            result[0] = false;
        }else {
            result[1] = false;
        }
    }

    return result;
}

2.2.3 校驗表示式

校驗表示式包括對除數為0,減法運算結果為負的情況的處理。

  • 若除數為0,替換除號為其他運算子。
  • 若減法結果為負,交換被減數和減數的位置。
  • CalAndVal()實際呼叫了getResult()方法,該方法獲取當前結點對應的運算結果並按以上規則校驗。

public String getResult(){
    //若有子節點,說明當前結點是運算子
    if(hasChild()){
        switch(str){
            case "+":
                return Calculator.add(getLchild().getResult(), getRchild().getResult());
            case "-":
                String subResult = Calculator.sub(getLchild().getResult(), getRchild().getResult());
                //若返回值為-1,交換左右孩子
                if(subResult.contains("-")){
                    swapChild();
                    return Calculator.sub(getLchild().getResult(), getRchild().getResult());
                }
                else {
                    return subResult;
                }
            case "x":
                return Calculator.mul(getLchild().getResult(), getRchild().getResult());
            case "÷":
                String divResult = Calculator.division(getLchild().getResult(), getRchild().getResult());
                //返回結果為-1,表示除數為0,替換運算子
                if(divResult.contains("-")){
                    while(str.equals("÷")){
                        //將當前運算子轉換為其他運算子
                        str = Ran.getOperator();
                    }
                    return this.getResult();
                }
                else {
                    return divResult;
                }
        }
    }
    //無子節點,說明當前結點是葉子結點,直接返回結點值
    return str;
}

3. 測試

輸入命令-n 6 -r 10(生成6道題目,最大值為10(不包括10)),生成的題目和答案如下:

題目:(在當前目錄下的expFile.txt中)

答案:(在當前目錄下的answer.txt中)

4. 小結

4.1 黃博曉

  • 跟別人合作開發要注意溝通好。

  • 資料結構很重要。

  • 使用搜索引擎有時可以極大的提高效率...

4.2 凌文宇

  • 基礎真的很重要,以後要繼續努力才能更好的和隊友合作

  • 全程大佬帶飛,以後要繼續加油,爭取不拖後腿

  • 事先的溝通與安排是必不可少的

參考連結

https://www.cnblogs.com/echoing/p/7878954.html

https://blog.csdn.net/kuku713/article/details/12684509