1. 程式人生 > >ngrinder(二) 壓力測試腳本groovy 實戰

ngrinder(二) 壓力測試腳本groovy 實戰

mes ++ cto ret tomat air 用例 base64 sse

前言

groovy腳本

ngrinder 的 groovy 腳本是順序結構的,用戶可通過編寫腳本執行過程中被預置的函數進行用戶操作,完成各種復雜的測試工作。

ngrinder 的進程與線程

ngrinder 使用進程和線程來模擬多個用戶。例如,如果您設置了如下的測試。只有一個代理將被激活,1個進程將被調用,然後這個進程將包括2個運行線程。每個線程的行為就像1個用戶。因此,2個虛擬用戶正在運行。如果將代理計數增加到2,則總共有4個虛擬用戶(Vusers)。

技術分享圖片

並發量=代理數x進程數x線程數
如果在Vuser per agent 中輸入總的虛擬用戶數時,nGrinder根據內部算法,會進行適當的計算,如輸入100,當agent數為1時, 會變成99,該算法可以通過 process_and_thread_policy.js 這個文件來修改。

若果agent 的內存4G以下的話,建議進程不要超過10個,線程數不要超過200.

  • 官方最新測試:4G內存的agent 最多可以模擬4000 虛擬用戶。

?

預置函數

依據上面對agent、進程與線程的解釋,就比較好理解ngrinder groovy 腳本的
結構了。
控制器將腳本分發給agent,每個agent按照算法啟動對應數量的進程,每個進程裏在啟動對應數量的線程,執行測試任務。

註解 描述 執行次數 用例
@BeforeProcess 在進程被調用之前執行的函數 每進程一次 加載被線程共享的資源文件,定義 公共變量等
@AfterProcess 在進程被終止之前執行該函數 每進程一次 關閉資源文件
@BeforeThread 在每個線程被調用之前執行的函數 每線程一次 登錄目標系統,建立線程內的一些值,例如,Cookie 處理
@AfterThread 在每個線程被終止之前執行的函數 每線程一次 退出系統
@Before 每個被 @Test 註解的方法被執行前應執行的函數 同虛擬用戶數 每個被 @Test 註解的方法的共享邏輯、變量設置
@After 每個被 @Test 註解的方法被執行後應執行的函數 同虛擬用戶數 很少使用
@Test 主測試行為,將被被執行多次 同虛擬用戶數 測試體

?

groovy 腳本實例

?

壓測實例

/*
這個腳本是對需要驗簽接口的壓測
*/
import static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import static org.hamcrest.Matchers.*
import net.grinder.plugin.http.HTTPRequest
import net.grinder.plugin.http.HTTPPluginControl
import net.grinder.script.GTest
import net.grinder.script.Grinder
import net.grinder.scriptengine.groovy.junit.GrinderRunner
import net.grinder.scriptengine.groovy.junit.annotation.BeforeProcess
import net.grinder.scriptengine.groovy.junit.annotation.BeforeThread
// import static net.grinder.util.GrinderUtils.* // You can use this if you‘re using nGrinder after 3.2.3
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith

import java.util.Date
import java.util.List
import java.util.ArrayList

import org.slf4j.LoggerFactory; 
import ch.qos.logback.classic.Level;

import HTTPClient.Cookie
import HTTPClient.CookieModule
import HTTPClient.HTTPResponse
import HTTPClient.NVPair

import java.text.SimpleDateFormat;

import org.apache.commons.codec.binary.Base64;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Arrays;
import org.apache.commons.lang.StringUtils;
import java.lang.StringBuilder

import java.io.UnsupportedEncodingException;

/**
 * A simple example using the HTTP plugin that shows the retrieval of a
 * single page via HTTP. 
 * 
 * This script is automatically generated by ngrinder.
 * 
 * @author admin
 */
@RunWith(GrinderRunner)
class TestRunner {

    public static GTest test
    public static HTTPRequest request
    // 定義全局變量
    public static NVPair[] params = []
    public static Cookie[] cookies = []
    public static String private_key
    public static String[] contents = []

    @BeforeProcess
    public static void beforeProcess() {
        HTTPPluginControl.getConnectionDefaults().timeout = 6000
        test = new GTest(1, "Test1")
        request = new HTTPRequest()
        // 獲取加密私鑰內容
        contents = new File("./resources/rsa_private_key_pkcs8.pem") as String[]
        StringBuilder private_str = new StringBuilder();
        for(int i=0;i<contents.length;i++){
            if (contents[i].charAt(0) != ‘-‘ && contents[i] != null) {
                private_str.append(contents[i]).append("\n");
            }
        }
        private_key = private_str.toString()
        //調試輸出
        grinder.logger.info("before process.");
    }

    @BeforeThread 
    public void beforeThread() {
        test.record(this, "test")
        grinder.statistics.delayReports=true;
        grinder.logger.info("before thread.");
    }
    //自定義函數,修改http頭數據
    private NVPair[] headers(post) {
        if( post != null ){
            post="&"+post
        }else{
            post=""
        }
        def appid = "1111111111111111111111"
        def appcode = "11111"
        long currentTime = System.currentTimeMillis()
        def json = "appId="+appid+"&appCode="+appcode+"&"+""+"timestamp="+currentTime+post
        def sign = getsign(json)
        //grinder.logger.info(currentTime.toString())
        return [
                new NVPair("Content-type", "application/json;charset=UTF-8"),
                new NVPair("appId", "1013f9d4e97026cb07e3fdea1b560f2f"),
                new NVPair("sign", sign),
                new NVPair("timestamp", currentTime.toString()),
                new NVPair("appCode", "11111")
        ];
    }
    //自定義函數,生成YYYY-MM-dd HH:mm:ss 格式的當天日期串
    def today()
    {
        String str = "";
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        Calendar lastDate = Calendar.getInstance();
        str = sdf.format(lastDate.getTime());
        return str;
    }

    // 自定義函數加簽 
    private getsign(post){
        //加簽算法 略
        return sign
    }

    @Before
    public void before() {
        request.setHeaders(headers())
        cookies.each { CookieModule.addCookie(it, HTTPPluginControl.getThreadHTTPClientContext()) }
        grinder.logger.info("before thread. init headers and cookies");
    }

    @Test
    public void test(){
        // 參數初始化
        def domain = "api.xxxx.com"
        def timestr=today()
        def json=‘{"eventCode":"1111111", "eventTime":"‘+timestr+‘", "channelCode":"1111111" , eventTime":"‘+timestr+‘"}}‘;
        //獲取簽名後的頭信息
        def head = headers("bizContent="+json)
        //grinder.logger.info(head.toString())
        HTTPResponse result = request.POST(‘http://‘+domain+‘/channel/v1/mot/receive‘,json.getBytes(), head)

        //HTTPResponse result = request.GET("http://106.14.8.248/phpapi.php", params)
        if(result.text.equals(new String(result.text.getBytes("iso8859-1"), "iso8859-1")))
        {
            result.text=new String(result.text.getBytes("iso8859-1"),"utf-8");
        }
        //調試輸出頭信息
        grinder.logger.info("result="+request.getHeaders())
        //調試輸出結果數據
        grinder.logger.info("result="+result.text)
        if (result.statusCode == 301 || result.statusCode == 302) {
            grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode); 
        } else {
            assertThat(result.statusCode, is(200));
        }
    }
}

ngrinder(二) 壓力測試腳本groovy 實戰