ngrinder(二) 壓力測試腳本groovy 實戰
阿新 • • 發佈:2018-10-19
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 實戰