【Jmeter】Jmeter使用AbstractJavaSamplerClient編寫自定義流程測試
一、前言
在上一篇部落格中,小編向大家簡單介紹了jmeter 的GUI介面的壓測使用步驟,通過這個步驟我們可以應付大部分的壓測問題了。很多介面都可以通過這種方式來得到壓測報告,根據壓測報告的資料來分析線上真正要使用多少臺機器。
但是有的時候,我們需要多個介面連起來測一個流程,如果使用簡單的GUI介面,就不能完成這個任務了,所以我們可以使用jmeter提供的ApacheJMeter_core.jar和ApacheJMeter_java.jar來編寫自定義的測試流程。
下面就簡單的介紹一下。
二、建立maven專案
新增maven依賴:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.soybean</groupId> <artifactId>redpacketJmeter</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_java</artifactId> <version>4.0</version> </dependency> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_java</artifactId> <version>4.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.37</version> </dependency> <dependency> <groupId>com.soybean</groupId> <artifactId>base-utils</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <!-- 用於打可執行jar包 --> <!-- 打包jar檔案時,配置manifest檔案,加入lib包的jar依賴 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <classesDirectory>target/classes/</classesDirectory> <archive> <manifest> <!-- 主函式的入口 --> <mainClass>com.soybean.Application</mainClass> <!-- 打包時 MANIFEST.MF檔案不記錄的時間戳版本 --> <useUniqueVersions>false</useUniqueVersions> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> </manifest> <manifestEntries> <Class-Path>.</Class-Path> </manifestEntries> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <type>jar</type> <includeTypes>jar</includeTypes> <!--<useUniqueVersions>false</useUniqueVersions> --> <outputDirectory> ${project.build.directory}/lib </outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
三、建立執行類
新建一個類,類名為BaseJmeterHttp,該類繼承AbstractJavaSamplerClient類,AbstractJavaSamplerClient存在於ApacheJMeter_java.jar這個JAR包中,引用即可呼叫。
BaseJmeterHttp類在繼承AbstractJavaSamplerClient類的時候,需要實現四個方法,分別是
-
setupTest():初始化方法,用於初始化效能測試時的每個執行緒;
-
getDefaultParameters():主要用於設定傳入的引數;
-
runTest():為效能測試時的執行緒執行體;
-
teardownTest():測試結束方法,用於結束效能測試中的每個執行緒。
具體程式碼:
package com.soybean.test.http; import com.soybean.common.logger.DushuLogger; import com.soybean.test.Entity.Packet; import com.soybean.test.Entity.PacketShareUrlVO; import com.soybean.test.util.HttpClientUtils; import com.soybean.test.util.HttpResult; import com.soybean.test.util.JsonUtil; import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.threads.JMeterVariables; import java.util.*; import java.util.concurrent.*; /** * <br> * Description: BaseJmeterHttpImpl<br> * Company : *********科技有限公司 <br> * Author : WangLei<br> * Date : 2018/10/15 14:20<br> * Modify : 修改日期 修改人員 修改說明 JIRA編號<br> * v1.0.0 2018/10/15 WangLei 新增 1001<br> ********************************************************************/ public class BaseJmeterHttp extends AbstractJavaSamplerClient { // private String ip ="http://gateway-java-bench.dushu.io"; private final String ip = "http://10.80.60.181:1111"; // 執行緒數 private final static int THREAD_SIZE = 30; private final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_SIZE); @Override public SampleResult runTest(JavaSamplerContext javaSamplerContext) { Packet packet = new Packet(); JMeterVariables jMeterVariables = javaSamplerContext.getJMeterVariables(); Iterator<Map.Entry<String, Object>> iterator = jMeterVariables.getIterator(); while (iterator.hasNext()) { Map.Entry<String, Object> entry = iterator.next();//把Object型強轉成int型 DushuLogger.info("獲取到的引數為:" + JsonUtil.toJSONObject(entry)); if (entry.getKey().equals("senderId")) { packet.setUserId((String) entry.getValue()); } if (entry.getKey().equals("orderNo")) { packet.setOrderNo((String) entry.getValue()); } if (entry.getKey().equals("token")) { packet.setToken(entry.getValue().toString().split("-")); } if (entry.getKey().equals("getId")) { packet.setGetterId(entry.getValue().toString().split("-")); } packet.setAppId("1001"); } boolean flag = true; String errorInfo = ""; SampleResult sr= new SampleResult(); List<Future<Integer>> futures = new ArrayList<>(); int countSuccess = 0; try { // 記錄程式執行時間以及執行結果 sr.sampleStart(); DushuLogger.info("接收到的引數:" + JsonUtil.toJSON(packet)); DushuLogger.info("開始建立紅包"); HttpResult httpResult = HttpClientUtils.postUrlAsJson(ip + "/redPacket-system/redPacket/generateRedPacket", JsonUtil.toJSONObject(packet)); if (!httpResult.isSuccess()) { DushuLogger.error("呼叫生成紅包介面失敗"); errorInfo = "呼叫生成紅包介面失敗"; flag = false; } Map map = JsonUtil.fromJSON(httpResult.getResponse(), Map.class); DushuLogger.info("建立紅包返回結果:" + JsonUtil.toJSON(map)); if (map.get("data") == null || !"0000".equals(map.get("status"))) { DushuLogger.error("建立紅包失敗"); errorInfo = "呼叫生成紅包介面成功,但是處理失敗:" + JsonUtil.toJSON(map) + " ,請求引數為:" + JsonUtil.toJSONObject(packet); flag = false; } else { //領紅包 final CountDownLatch latch = new CountDownLatch(THREAD_SIZE); for (int i = 0; i < THREAD_SIZE; i++) { PacketShareUrlVO packetShareUrlVO = new PacketShareUrlVO(); packetShareUrlVO.setRedPacketId(Long.parseLong(map.get("data").toString())); packetShareUrlVO.setAppId(packet.getAppId()); packetShareUrlVO.setToken(packet.getToken()[i]); packetShareUrlVO.setUserId(packet.getGetterId()[i]); DushuLogger.info(JsonUtil.toJSON(packetShareUrlVO)); DushuLogger.info(packetShareUrlVO); Callable<Integer> task1 = () -> { String resultInfo = ""; //領紅包 HttpResult httpResultSmall = HttpClientUtils.postUrlAsJson(ip + "/redPacket-system/redPacket/bindSmallPacketAndUser", JsonUtil.toJSONObject(packetShareUrlVO)); if (!httpResultSmall.isSuccess()) { DushuLogger.error("【領紅包失敗】呼叫領紅包介面失敗:" + JsonUtil.toJSONObject(packetShareUrlVO)); resultInfo = "【領紅包失敗】呼叫領紅包介面失敗:" + JsonUtil.toJSONObject(packetShareUrlVO); } Map smallPacketMap = JsonUtil.fromJSON(httpResultSmall.getResponse(), Map.class); if (smallPacketMap.get("status") == null || !"0000".equals(smallPacketMap.get("status"))) { DushuLogger.error("【領紅包失敗】呼叫領紅包介面成功,但是處理失敗,入參為:" + JsonUtil.toJSONObject(packetShareUrlVO) + "處理結果為:" + JsonUtil.toJSONObject(smallPacketMap)); resultInfo = "【領紅包失敗】呼叫領紅包介面成功,但是處理失敗,入參為:" + JsonUtil.toJSONObject(packetShareUrlVO) + "處理結果為:" + JsonUtil.toJSONObject(smallPacketMap); } else { //更新會期 Map<String, Object> addVipParamMap = new HashMap<>(); addVipParamMap.put("token", packetShareUrlVO.getToken()); HttpResult addVipTermResult = HttpClientUtils.postUrlAsJson(ip + "/redPacket-system/redPacket/addVipTerm", addVipParamMap); if (!addVipTermResult.isSuccess()) { DushuLogger.error("【更新會期失敗】呼叫更新會期介面失敗:" + JsonUtil.toJSONObject(addVipParamMap)); resultInfo = "【更新會期失敗】呼叫更新會期介面失敗:" + JsonUtil.toJSONObject(addVipParamMap); } Map addVipMap = JsonUtil.fromJSON(addVipTermResult.getResponse(), Map.class); if (addVipMap.get("status") == null || !"0000".equals(addVipMap.get("status"))) { DushuLogger.error("【更新會期失敗】呼叫更新會期介面成功,但是處理失敗,入參:" + JsonUtil.toJSONObject(addVipParamMap) + "處理結果為:" + JsonUtil.toJSONObject(addVipMap)); resultInfo = "【更新會期失敗】呼叫更新會期介面成功,但是處理失敗,入參:" + JsonUtil.toJSONObject(addVipParamMap) + "處理結果為:" + JsonUtil.toJSONObject(addVipMap); } else { DushuLogger.info("更新會期成功"); } } latch.countDown(); return "".equals(resultInfo) ? 1 : 0; }; futures.add(executorService.submit(task1)); } latch.await(); } //統計結果 for (Future f : futures) { countSuccess = countSuccess + (int) f.get(); } DushuLogger.info("【統計結果】一共搶紅包的人有:" + THREAD_SIZE + "個,其中成功搶到紅包的有:" + countSuccess + "個"); flag = countSuccess == 15; if (!flag) { errorInfo =errorInfo + "請求引數為:" + JsonUtil.toJSONObject(packet) +"【統計結果】一共搶紅包的人有:" + THREAD_SIZE + "個,其中成功搶到紅包的有:" + countSuccess + "個"; } sr.setSuccessful(flag); } catch (Exception e) { sr.setSuccessful(false); } finally { sr.sampleEnd(); } //將資料列印到檢視結果樹當中 sr.setResponseData(flag ? "紅包流程執行成功,共有 " + countSuccess + " 個成功搶到紅包,請求引數為:" + JsonUtil.toJSONObject(packet) : "紅包流程執行失敗,原因:" + errorInfo, null); sr.setDataType(SampleResult.TEXT); return sr; } }
四、Jmeter執行分析
1、將上述程式碼打包成jar包,生成的包名稱為redpacketJmeter.jar,將jar包拷貝到Jmeter的安裝目錄lib/ext下面。
2、把依賴的jar包都拷貝到Jmeter安裝目錄的lib下,替換原有的同名jar包
2、執行Jmeter,新增執行緒組及java請求,顯示如下:
3、新增監聽器,這裡我們新增檢視結果樹和聚合報告就好。
4、結果顯示如下圖:
五、小結
Jmeter是用java語言開發的,所以我們可以用java開發出程式碼,直接使用,當然其中還有一些引數是要用的,比如讀取csv檔案的內容等。希望大家可以學習到知識。