性能工具之Ngrinder之Get請求腳本編寫
- 2019 年 10 月 29 日
- 筆記
原文作者:李文

背景:做性能測試腳本是一個實際下功夫的地方,工作中常見也就是key-value,json方式比較多,那麼 nGrinder腳本咱們怎麼編寫以下簡單介紹。
首先,通過Springboot編寫一個工程實現增刪改查,通過get請求獲取:
http://localhost:8888/findinfo?username=600128

該工程controller層中用最簡單get請求查詢數據,該代碼為:
@GetMapping("/findinfo") @ResponseBody public List<UserTable> findUser(UserTable userInfo) { List<UserTable> UserInfo = userService.findinfo(userInfo); return UserInfo; }
@ResponseBody註解會自動轉換json顯示到頁面。該工程很簡單,就不展示其他代碼,大家在做練習的時候,可以找自己公司的項目或者自己寫一個demo工程,進行練習。
接口層:
public interface UserService {
List<UserTable> findinfo(UserTable userInfo);
}
實現層:
@Service public class UserServiceImpl implements UserService {
@Override public List<UserTable> findinfo(UserTable userInfo) { UserTableExample example = new UserTableExample(); UserTableExample.Criteria criteria = example.createCriteria(); criteria.andUsernameEqualTo(userInfo.getUsername()); return userTableMapper.selectByExample(example); }}
數據庫dao層:該層通過generator插件生成

generator插件參考代碼:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!-- 數據庫驅動:選擇你的本地硬盤上面的數據庫驅動包--> <classPathEntry location="..mysql-managerlibmysql-connector-java-5.1.6.jar"/> <context id="DB2Tables" targetRuntime="MyBatis3"> <commentGenerator> <property name="suppressDate" value="true"/> <!-- 是否去除自動生成的注釋 true:是 :false:否 --> <property name="suppressAllComments" value="true"/> </commentGenerator> <!--數據庫鏈接URL,用戶名、密碼 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/7d" userId="root" password="123456"> </jdbcConnection> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- 生成模型的包名和位置--> <javaModelGenerator targetPackage="com.sevendgrop.pojo" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- 生成映射文件的包名和位置--> <sqlMapGenerator targetPackage="mapping" targetProject="src/main/resources"> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <!-- 生成DAO的包名和位置--> <javaClientGenerator type="XMLMAPPER" targetPackage="com.sevendgrop.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <!-- 要生成的表 tableName是數據庫中的表名或視圖名 domainObjectName是實體類名--> <table schema="" tableName="case_table"></table> </context> </generatorConfiguration>
Pom.xml配置:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> </configuration> </plugin> <!-- mybatis generator 自動生成代碼插件--> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile> <overwrite>true</overwrite> <verbose>true</verbose> </configuration> </plugin> </plugins> </build>
maven插件編寫參考:
mybatis-generator:generate -e

配置上面後點擊:

運行即可就能生存數據庫鏈接sql語句。
打開上一節使用源碼部署的工程,在介紹源碼運行腳本地方新建一個腳本,參考如下代碼修改成自己練習的腳本。
如圖:

在nGrinder中新建的腳本編寫如下代碼:
import org.junit.FixMethodOrder 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 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 HTTPClient.Cookie import HTTPClient.CookieModule import HTTPClient.HTTPResponse import HTTPClient.NVPair /** * @Title: PostGetDemo * @Description: get請求 * @author liwen * @date 2019/10/23 / 16:22 */ @RunWith(GrinderRunner) class PostGetDemo {
public static GTest test // 定義 HTTPRequest 靜態變量 request,用於發送 HTTP 請求 public static HTTPRequest request // 定義 NVPair 數組 headers ,用於存放通用的請求頭數據 public static NVPair[] headers = [] // 定義 NVPair 數組 params ,用於存放請求參數數據 public static NVPair[] params = [] // 定義 Cookie 數組 cookies ,用於存放通用的 cookie 數據 public static Cookie[] cookies = [] @BeforeProcess public static void beforeProcess() { // 設置請求響應超時時間(ms) HTTPPluginControl.getConnectionDefaults().timeout = 6000 // 創建GTest對象,第一個參數1代表有多個請求/事務時的執行順序ID, 第二個參數是請求/事務的名稱,會顯示在summary結果中,有多個請求/事務時, 要創建多個GTest對象 test = new GTest(1, "localhost:8888") //創建 HTTPRequest 對象,用於發起 HTTP 請求 request = new HTTPRequest() // Set header datas List<NVPair> headerList = new ArrayList<NVPair>() headerList.add(new NVPair("Content-Type", "application/x-www-form-urlencoded")) headerList.add(new NVPair("Connection", "keep-alive")) headers = headerList.toArray() // Set param datas List<NVPair> paramList = new ArrayList<NVPair>() paramList.add(new NVPair("username", "600128")) params = paramList.toArray() // Set cookie datas List<Cookie> cookieList = new ArrayList<Cookie>() cookieList.add(new Cookie("Cookie", "Idea-96adee05=87213429-7753-4183-99d0-c1c3362ce5d0; Hm_lvt_eb54a8c74fe4cb91ad8ca004e48cd3f9=1545999605; Pycharm-5b892e29=6772acb9-e87b-4d37-98e3-bff82e412d17; csrftoken=KDr8oYtD0gKh7UbS5fWSROzEOX6rjX3CN26pz9PQu9AczLMGFhw53ZrjAhoIOTE7; Hm_lvt_f2c884fc06fca522c4105429259b8a73=1558506687; Webstorm-173930bd=052a6829-b802-4eb0-a1e2-e348f1012604; _ga=GA1.1.1061327805.1562753587; UM_distinctid=16c02a6b049e1-02b6b032525db4-e343166-144000-16c02a6b04a120; CNZZDATA4617777=cnzz_eid%3D1637582848-1563412391-%26ntime%3D1563412391; Hm_lvt_0cb375a2e834821b74efffa6c71ee607=1563412574; bad_id22bdcd10-6250-11e8-917f-9fb8db4dc43c=a32ab941-a8f9-11e9-9989-93f7b4f25032; Idea-2b3f02ca=87213429-7753-4183-99d0-c1c3362ce5d0; cookie_lang=0; JSESSIONID=801DE60D9A00C391F80ABE0CD94A6E25", "localhost:8888", "", new Date(), true)) cookies = cookieList.toArray() grinder.logger.info("before process."); } @BeforeThread public void beforeThread() { // 註冊事件,啟動test,第二個參數要與@Test註解的方法名保持一致, 有多個請求/事務時,要註冊多個事件 test.record(this, "test") //配置延遲報告統計結果 grinder.statistics.delayReports = true; grinder.logger.info("before thread."); } @Before public void before() { //在這裡可以添加headers屬性和cookies // request.setHeaders(headers) cookies.each { CookieModule.addCookie(it, HTTPPluginControl.getThreadHTTPClientContext()) } grinder.logger.info("before thread. init headers and cookies"); } @Test public void test() { // 發送GET請求 HTTPResponse result = request.GET("http://localhost:8888/findinfo", params) def text = result.getText() grinder.logger.info(text) // 斷言HTTP請求狀態碼 assertThat(result.statusCode, is(200)) }
}
再次運行:

點擊運行配置加上:
-javaagent:D:mavenrepositorynetsfgrindergrinder-dcr-agent3.9.1grinder-dcr-agent-3.9.1.jar
配置說明如下:

之後點擊運行即可:

結果如下:

關鍵點需要注意這裡:
List<NVPair> headerList = new ArrayList<NVPair>() headerList.add(new NVPair("Content-Type", "application/x-www-form-urlencoded")) headerList.add(new NVPair("Connection", "keep-alive")) headers = headerList.toArray()
頭信息:
public static NVPair[] headers = [] public static NVPair[] params = [] public static Cookie[] cookies = []
查看源碼就知道怎麼傳值,這裡列舉cookie源碼傳值說明:

通過源碼查看得知如果傳cookie需要new cookie實列通過構造方法進行傳值入:
List<Cookie> cookieList = new ArrayList<Cookie>() cookieList.add(new Cookie("Cookie", "Idea-96adee05=87213429-7753-4183-99d0-c1c3362ce5d0; Hm_lvt_eb54a8c74fe4cb91ad8ca004e48cd3f9=1545999605; Pycharm-5b892e29=6772acb9-e87b-4d37-98e3-bff82e412d17; csrftoken=KDr8oYtD0gKh7UbS5fWSROzEOX6rjX3CN26pz9PQu9AczLMGFhw53ZrjAhoIOTE7; Hm_lvt_f2c884fc06fca522c4105429259b8a73=1558506687; Webstorm-173930bd=052a6829-b802-4eb0-a1e2-e348f1012604; _ga=GA1.1.1061327805.1562753587; UM_distinctid=16c02a6b049e1-02b6b032525db4-e343166-144000-16c02a6b04a120; CNZZDATA4617777=cnzz_eid%3D1637582848-1563412391-%26ntime%3D1563412391; Hm_lvt_0cb375a2e834821b74efffa6c71ee607=1563412574; bad_id22bdcd10-6250-11e8-917f-9fb8db4dc43c=a32ab941-a8f9-11e9-9989-93f7b4f25032; Idea-2b3f02ca=87213429-7753-4183-99d0-c1c3362ce5d0; cookie_lang=0; JSESSIONID=801DE60D9A00C391F80ABE0CD94A6E25", "localhost:8888", "", new Date(), true)) cookies = cookieList.toArray() grinder.logger.info("before process.");
List<Cookie> cookieList = new ArrayList<Cookie>() cookieList.add(new Cookie("Cookie", "Idea-96adee05=87213429-7753-4183-99d0-c1c3362ce5d0; Hm_lvt_eb54a8c74fe4cb91ad8ca004e48cd3f9=1545999605; Pycharm-5b892e29=6772acb9-e87b-4d37-98e3-bff82e412d17; csrftoken=KDr8oYtD0gKh7UbS5fWSROzEOX6rjX3CN26pz9PQu9AczLMGFhw53ZrjAhoIOTE7; Hm_lvt_f2c884fc06fca522c4105429259b8a73=1558506687; Webstorm-173930bd=052a6829-b802-4eb0-a1e2-e348f1012604; _ga=GA1.1.1061327805.1562753587; UM_distinctid=16c02a6b049e1-02b6b032525db4-e343166-144000-16c02a6b04a120; CNZZDATA4617777=cnzz_eid%3D1637582848-1563412391-%26ntime%3D1563412391; Hm_lvt_0cb375a2e834821b74efffa6c71ee607=1563412574; bad_id22bdcd10-6250-11e8-917f-9fb8db4dc43c=a32ab941-a8f9-11e9-9989-93f7b4f25032; Idea-2b3f02ca=87213429-7753-4183-99d0-c1c3362ce5d0; cookie_lang=0; JSESSIONID=801DE60D9A00C391F80ABE0CD94A6E25", "localhost:8888", "", new Date(), true)) cookies = cookieList.toArray()
查看@BeforeThread註解下會執行 test.record方法源碼如下:
/** * Instrument the supplied {@code target} object's method which has the given name. Subsequent * calls to {@code target}'s given method will be recorded against the statistics for this * {@code Test}. * 提供的具有給定名稱的{@code target}對象方法。 後繼的對{@code target}給定方法的調用將根據此方法的統計信息進行記錄 * @param target Object to instrument. * @param methodName method name to instrument * @throws NonInstrumentableTypeException If {@code target} could not be instrumented. * @since 3.2.1 */ public final void record(Object target, String methodName) throws NonInstrumentableTypeException { if (StringUtils.isNotEmpty(context)) { record(target, new MethodNameFilter(methodName)); } }
解釋:target 指腳本對象,這裡是this;
methodName 是需要統計的方法名,通常都是被 @Test 注釋的方法。如果未配置,方法會正常執行,但是沒有統計結果數據;
以下代碼是可以複製出來修改的代碼
import org.junit.FixMethodOrder 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 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 HTTPClient.Cookie import HTTPClient.CookieModule import HTTPClient.HTTPResponse import HTTPClient.NVPair /** * @Title: PostGetDemo* @Description: get請求* @author liwen * @date 2019/10/23 / 16:22 */ @RunWith(GrinderRunner) class PostGetDemo { public static GTest test // 定義 HTTPRequest 靜態變量 request,用於發送 HTTP 請求 public static HTTPRequest request // 定義 NVPair 數組 headers ,用於存放通用的請求頭數據 public static NVPair[] headers = [] // 定義 NVPair 數組 params ,用於存放請求參數數據 public static NVPair[] params = [] // 定義 Cookie 數組 cookies ,用於存放通用的 cookie 數據 public static Cookie[] cookies = [] @BeforeProcess public static void beforeProcess() { // 設置請求響應超時時間(ms) HTTPPluginControl.getConnectionDefaults().timeout = 6000 // 創建GTest對象,第一個參數1代表有多個請求/事務時的執行順序ID,第二個參數是請求/事務的名稱,會顯示在summary結果中,有多個請求/事務時,要創建多個GTest對象 test = new GTest(1, "localhost:8888") //創建 HTTPRequest 對象,用於發起 HTTP 請求 request = new HTTPRequest() // Set header datas List<NVPair> headerList = new ArrayList<NVPair>() headerList.add(new NVPair("Content-Type", "application/x-www-form-urlencoded")) headerList.add(new NVPair("Connection", "keep-alive")) headers = headerList.toArray() // Set param datas List<NVPair> paramList = new ArrayList<NVPair>() paramList.add(new NVPair("username", "600128")) params = paramList.toArray() // Set cookie datas List<Cookie> cookieList = new ArrayList<Cookie>() cookieList.add(new Cookie("Cookie", "Idea-96adee05=87213429-7753-4183-99d0-c1c3362ce5d0; Hm_lvt_eb54a8c74fe4cb91ad8ca004e48cd3f9=1545999605; Pycharm-5b892e29=6772acb9-e87b-4d37-98e3-bff82e412d17; csrftoken=KDr8oYtD0gKh7UbS5fWSROzEOX6rjX3CN26pz9PQu9AczLMGFhw53ZrjAhoIOTE7; Hm_lvt_f2c884fc06fca522c4105429259b8a73=1558506687; Webstorm-173930bd=052a6829-b802-4eb0-a1e2-e348f1012604; _ga=GA1.1.1061327805.1562753587; UM_distinctid=16c02a6b049e1-02b6b032525db4-e343166-144000-16c02a6b04a120; CNZZDATA4617777=cnzz_eid%3D1637582848-1563412391-%26ntime%3D1563412391; Hm_lvt_0cb375a2e834821b74efffa6c71ee607=1563412574; bad_id22bdcd10-6250-11e8-917f-9fb8db4dc43c=a32ab941-a8f9-11e9-9989-93f7b4f25032; Idea-2b3f02ca=87213429-7753-4183-99d0-c1c3362ce5d0; cookie_lang=0; JSESSIONID=801DE60D9A00C391F80ABE0CD94A6E25", "localhost:8888", "", new Date(), true)) cookies = cookieList.toArray() grinder.logger.info("before process."); } @BeforeThread public void beforeThread() { // 註冊事件,啟動test,第二個參數要與@Test註解的方法名保持一致,有多個請求/事務時,要註冊多個事件 test.record(this, "test") //配置延遲報告統計結果 grinder.statistics.delayReports = true; grinder.logger.info("before thread."); } @Before public void before() { //在這裡可以添加headers屬性和cookies // request.setHeaders(headers) cookies.each { CookieModule.addCookie(it, HTTPPluginControl.getThreadHTTPClientContext()) } grinder.logger.info("before thread. init headers and cookies"); } @Test public void test() { // 發送GET請求 HTTPResponse result = request.GET("http://localhost:8888/findinfo", params) def text = result.getText() grinder.logger.info(text) // 斷言HTTP請求狀態碼 assertThat(result.statusCode, is(200)) }
世上沒有一件工作不幸苦,沒有一處人事不複雜,學會低調,取捨間必有得失,不用太計較,學者踏實而務實,越努力越幸運,學會感恩。人生必是善人善報。若是美好,叫做精彩,若是糟糕,叫做經歷。