jmeter性能测试及报告功能全量提交

This commit is contained in:
liangdaliang
2025-04-16 18:26:17 +08:00
parent f200aeec4e
commit adbceebec5
8 changed files with 535 additions and 176 deletions

View File

@@ -14,7 +14,7 @@ public class JmeterGroupRequest {
private Integer errorOperType;
private Integer executeType;
private Integer rampUpSeconds;
private Integer pressureSecond;
private Long pressureSecond;
private Integer loopCount;
private Integer rpsStatus;
private Integer rpsLimit;
@@ -61,11 +61,11 @@ public class JmeterGroupRequest {
this.rampUpSeconds = rampUpSeconds;
}
public Integer getPressureSecond() {
public Long getPressureSecond() {
return pressureSecond;
}
public void setPressureSecond(Integer pressureSecond) {
public void setPressureSecond(Long pressureSecond) {
this.pressureSecond = pressureSecond;
}

View File

@@ -11,6 +11,11 @@ public class JmeterRequest {
*/
private Long id;
/**
* 用例步骤名称
*/
private String testCaseName;
/**
* 请求完整url
*/
@@ -46,6 +51,14 @@ public class JmeterRequest {
*/
private String jmeterHomePath;
public String getTestCaseName() {
return testCaseName;
}
public void setTestCaseName(String testCaseName) {
this.testCaseName = testCaseName;
}
public Long getId() {
return id;
}

View File

@@ -0,0 +1,96 @@
package com.test.common.core.domain.model;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* @author liangdaliang
* @DescriptionJmeter汇总报告实体对象
* @date 2025-04-16 14:24
*/
public class LabelStatsEntity {
private String label;
private Long samples = 0L;
private Long average = 0L;
private Long min = 0L;
private Long max = 0L;
@JsonFormat(shape = JsonFormat.Shape.NUMBER, pattern = "0.00")
private Double errorRate = 0d;
@JsonFormat(shape = JsonFormat.Shape.NUMBER, pattern = "0.0")
private Double throughput = 0d;
@JsonFormat(shape = JsonFormat.Shape.NUMBER, pattern = "0.00")
private Double receivedKBPerSec = 0d;
@JsonFormat(shape = JsonFormat.Shape.NUMBER, pattern = "0.00")
private Double sentKBPerSec = 0d;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Long getSamples() {
return samples;
}
public void setSamples(Long samples) {
this.samples = samples;
}
public Long getAverage() {
return average;
}
public void setAverage(Long average) {
this.average = average;
}
public Long getMin() {
return min;
}
public void setMin(Long min) {
this.min = min;
}
public Long getMax() {
return max;
}
public void setMax(Long max) {
this.max = max;
}
public Double getErrorRate() {
return errorRate;
}
public void setErrorRate(Double errorRate) {
this.errorRate = errorRate;
}
public Double getThroughput() {
return throughput;
}
public void setThroughput(Double throughput) {
this.throughput = throughput;
}
public Double getReceivedKBPerSec() {
return receivedKBPerSec;
}
public void setReceivedKBPerSec(Double receivedKBPerSec) {
this.receivedKBPerSec = receivedKBPerSec;
}
public Double getSentKBPerSec() {
return sentKBPerSec;
}
public void setSentKBPerSec(Double sentKBPerSec) {
this.sentKBPerSec = sentKBPerSec;
}
}

View File

@@ -6,6 +6,7 @@ import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
import com.test.common.core.domain.model.JmeterGroupRequest;
import com.test.common.core.domain.model.JmeterRequest;
import com.test.common.core.domain.model.LabelStatsEntity;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.gui.ArgumentsPanel;
import org.apache.jmeter.control.LoopController;
@@ -30,6 +31,7 @@ import org.apache.jmeter.threads.gui.ThreadGroupGui;
import org.apache.jmeter.timers.ConstantThroughputTimer;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
@@ -46,10 +48,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* @author liangdaliang
@@ -65,14 +64,18 @@ public class JMeterGroupUtil {
* @param jmeterGroupRequest
* @return
*/
public static Map<String, String> getJmeterResult(JmeterGroupRequest jmeterGroupRequest) {
public static List<LabelStatsEntity> getJmeterResult(JmeterGroupRequest jmeterGroupRequest) {
Long id = jmeterGroupRequest.getTestCaseId();
Integer concurrentThreads = jmeterGroupRequest.getConcurrentThreads();
Integer loopCount = jmeterGroupRequest.getLoopCount();
Integer errorOperType = jmeterGroupRequest.getErrorOperType();
Integer executeType = jmeterGroupRequest.getExecuteType();
Integer rampUpSeconds = jmeterGroupRequest.getRampUpSeconds();
Long pressureSecond = jmeterGroupRequest.getPressureSecond();
Integer rpsStatus = jmeterGroupRequest.getRpsStatus();
Integer rpsLimit = jmeterGroupRequest.getRpsLimit();
String jmeterHomePath = jmeterGroupRequest.getJmeterHomePath();
Map<String, String> result = null;
List<LabelStatsEntity> result = null;
try {
// 1. 初始化 JMeter
JMeterUtils.loadJMeterProperties(jmeterHomePath + "/bin/jmeter.properties");
@@ -83,7 +86,7 @@ public class JMeterGroupUtil {
TestPlan testPlan = createTestPlan("Dynamic Test Plan");
// 3. 创建线程组
org.apache.jmeter.threads.ThreadGroup threadGroup = createThreadGroup(concurrentThreads, loopCount);
org.apache.jmeter.threads.ThreadGroup threadGroup = createThreadGroup(concurrentThreads, loopCount, errorOperType, executeType, rampUpSeconds, pressureSecond);
// 4. 获取结果:如汇总报告、查看结果树
String replayLogPath = jmeterHomePath + "/p_replay_result"+ id +".log";
@@ -93,8 +96,9 @@ public class JMeterGroupUtil {
// 5. 构建测试树新增每个用例下的http请求节点
HashTree firstTreeTestPlan = new HashTree();
HashTree secondHashTree = new HashTree();
HashTree thirdHashTree = new HashTree();
ListedHashTree thirdHashTree = new ListedHashTree();
dealHttpHashTreeWithRequestList(thirdHashTree, jmeterGroupRequest.getJmeterRequestList());
// 6. 获取设置吞吐量
ConstantThroughputTimer constantThroughputTimer = null;
thirdHashTree.add(resultCollector);
@@ -111,7 +115,7 @@ public class JMeterGroupUtil {
SaveService.saveTree(firstTreeTestPlan, new FileOutputStream(jmxFile));
// 8. 运行 JMeter 测试
File resultFile = new File(jmeterHomePath + "/p_replay_result"+ id +".log");
File resultFile = new File(jmeterHomePath + "/p_summary_report"+ id +".log");
if (resultFile.exists()) {
resultFile.delete();
}
@@ -123,7 +127,7 @@ public class JMeterGroupUtil {
Thread.sleep(1000); // 每隔 1 秒检查一次
}
// 9. 获取响应结果
// result = getResultMessageFromFile(jmeterHomePath + "/p_replay_result"+ id +".log");
result = getResultMessageFromFile(jmeterHomePath + "/p_summary_report"+ id +".log");
// if (result != null) {
// result.put("requestHeader", requestHeaderJson);
// result.put("requestBody", requestBody);
@@ -141,7 +145,7 @@ public class JMeterGroupUtil {
* @param thirdHashTree
* @param requestList
*/
private static void dealHttpHashTreeWithRequestList(HashTree thirdHashTree, List<JmeterRequest> requestList) {
private static void dealHttpHashTreeWithRequestList(ListedHashTree thirdHashTree, List<JmeterRequest> requestList) {
for (JmeterRequest jmeterRequest : requestList) {
try {
HashTree fourHashTree = new HashTree();
@@ -151,12 +155,12 @@ public class JMeterGroupUtil {
String method = jmeterRequest.getMethod().toUpperCase();
String requestBody = jmeterRequest.getRequestBody();
String requestParams = jmeterRequest.getRequestParams();
String requestHeaderJson = "";
Map<String, String> requestParamsMap = convertJsonStringToMap(requestParams);
String requestHeader = jmeterRequest.getRequestHeader();
HTTPSamplerProxy httpSampler = null;
if ("POST".equals(method)) {
httpSampler = createPostHttpSampler(
jmeterRequest.getTestCaseName(),
url, // 请求地址
port,
requestBody, // 请求体
@@ -164,6 +168,7 @@ public class JMeterGroupUtil {
);
} else {
httpSampler = createGetHttpSampler(
jmeterRequest.getTestCaseName(),
url, // 请求地址
port,
requestParamsMap // 查询参数
@@ -186,14 +191,18 @@ public class JMeterGroupUtil {
String value = entry.get("value");
headerMap.put(key, value);
}
// requestHeaderJson = gson.toJson(headerMap);
} catch (Exception e) {
logger.error("requestHeader gson.fromJson异常requestHeader" + requestHeader, e);
}
}
HeaderManager headerManager = createHeaderManager(headerMap);
fourHashTree.add(headerManager);
thirdHashTree.add(httpSampler, fourHashTree);
if (headerManager != null) {
fourHashTree.add(headerManager);
thirdHashTree.add(httpSampler, fourHashTree);
} else {
thirdHashTree.add(httpSampler);
}
} catch (Exception e) {
String info = "url:" + jmeterRequest.getUrl() + ", port:" + jmeterRequest.getPort();
logger.error(info + " dealHttpHashTreeWithRequest异常", e);
@@ -218,49 +227,73 @@ public class JMeterGroupUtil {
* 创建线程组
* @param numThreads
* @param loops
* @param errorOperType
* @param executeType
* @param rampUpSeconds
* @param pressureSecond
* @return
*/
private static org.apache.jmeter.threads.ThreadGroup createThreadGroup(int numThreads, int loops) {
private static org.apache.jmeter.threads.ThreadGroup createThreadGroup(Integer numThreads, Integer loops, Integer errorOperType, Integer executeType, Integer rampUpSeconds, Long pressureSecond) {
org.apache.jmeter.threads.ThreadGroup threadGroup = new org.apache.jmeter.threads.ThreadGroup();
threadGroup.setName("Thread Group");
threadGroup.setNumThreads(numThreads);
threadGroup.setRampUp(1);
threadGroup.setSamplerController(createLoopController(loops));
threadGroup.setRampUp(rampUpSeconds);
threadGroup.setDelay(0);
if (executeType == 1) {
threadGroup.setDuration(pressureSecond);
} else {
threadGroup.setProperty(new TestElementProperty(ThreadGroup.MAIN_CONTROLLER, createLoopController(loops)));
}
if (errorOperType == 1) {
threadGroup.setProperty(new StringProperty(ThreadGroup.ON_SAMPLE_ERROR, "continue"));
} else if (errorOperType == 2) {
threadGroup.setProperty(new StringProperty(ThreadGroup.ON_SAMPLE_ERROR, "startnextloop"));
} else if (errorOperType == 3) {
threadGroup.setProperty(new StringProperty(ThreadGroup.ON_SAMPLE_ERROR, "stopthread"));
} else if (errorOperType == 4) {
threadGroup.setProperty(new StringProperty(ThreadGroup.ON_SAMPLE_ERROR, "stoptest"));
} else if (errorOperType == 5) {
threadGroup.setProperty(new StringProperty(ThreadGroup.ON_SAMPLE_ERROR, "stoptestnow"));
}
threadGroup.setScheduler(false);
threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName());
threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName());
threadGroup.setProperty(new BooleanProperty(TestElement.ENABLED, true));
return threadGroup;
}
/**
* 创建轮询控制器
* @param loops
* @return
*/
private static LoopController createLoopController(int loops) {
private static LoopController createLoopController(Integer loops) {
LoopController loopController = new LoopController();
loopController.setLoops(loops);
loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());
loopController.setProperty(TestElement.GUI_CLASS, LoopControlPanel.class.getName());
loopController.initialize();
loopController.setContinueForever(false);
loopController.setProperty(new StringProperty(TestElement.GUI_CLASS, LoopControlPanel.class.getName()));
loopController.setProperty(new StringProperty(TestElement.TEST_CLASS, LoopController.class.getName()));
loopController.setProperty(new StringProperty(TestElement.NAME, "循环控制器"));
loopController.setProperty(new BooleanProperty(TestElement.ENABLED, true));
loopController.setProperty(new IntegerProperty(LoopController.LOOPS, loops));
return loopController;
}
/**
* 创建get请求方式
* @param testCaseName
* @param urlString
* @param port
* @param requestParams
* @return
* @throws MalformedURLException
*/
private static HTTPSamplerProxy createGetHttpSampler(String urlString, int port, Map<String, String> requestParams) throws MalformedURLException {
private static HTTPSamplerProxy createGetHttpSampler(String testCaseName, String urlString, int port, Map<String, String> requestParams) throws MalformedURLException {
URL url = new URL(urlString);
// 提取域名(不包括端口号)
String domain = url.getHost();
// 提取路径(不包括查询参数和片段标识符)
String path = url.getPath();
HTTPSamplerProxy httpSampler = new HTTPSamplerProxy();
httpSampler.setName("HTTP Request Test");
httpSampler.setName(testCaseName);
httpSampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
httpSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName());
httpSampler.setDomain(domain); // 提取域名
@@ -334,6 +367,7 @@ public class JMeterGroupUtil {
/**
* 创建post请求方式
* @param testCaseName
* @param urlString
* @param port
* @param requestBody
@@ -341,7 +375,7 @@ public class JMeterGroupUtil {
* @return
* @throws MalformedURLException
*/
private static HTTPSamplerProxy createPostHttpSampler(String urlString, int port, String requestBody, Map<String, String> requestParams) throws MalformedURLException {
private static HTTPSamplerProxy createPostHttpSampler(String testCaseName, String urlString, int port, String requestBody, Map<String, String> requestParams) throws MalformedURLException {
URL url = new URL(urlString);
// 提取域名(不包括端口号)
String domain = url.getHost();
@@ -349,7 +383,7 @@ public class JMeterGroupUtil {
String path = url.getPath();
// int portTest = url.getPort();
HTTPSamplerProxy httpSampler = new HTTPSamplerProxy();
httpSampler.setName("POST JSON Request");
httpSampler.setName(testCaseName);
httpSampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
httpSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName());
httpSampler.setDomain(domain);
@@ -376,18 +410,21 @@ public class JMeterGroupUtil {
* @return
*/
private static HeaderManager createHeaderManager(Map<String, String> headerMap) {
if (headerMap == null || headerMap.isEmpty()) {
return null;
}
HeaderManager headerManager = new HeaderManager();
headerManager.setName("HTTP Header Manager");
headerManager.setProperty(TestElement.TEST_CLASS, HeaderManager.class.getName());
headerManager.setProperty(TestElement.GUI_CLASS, HeaderPanel.class.getName());
// 添加默认 Content-Type 请求头
if (!headerMap.containsKey("Content-Type")) {
Header contentTypeHeader = new Header();
contentTypeHeader.setName("Content-Type");
contentTypeHeader.setValue("application/json; charset=UTF-8");
headerManager.add(contentTypeHeader);
}
// if (!headerMap.containsKey("Content-Type")) {
// Header contentTypeHeader = new Header();
// contentTypeHeader.setName("Content-Type");
// contentTypeHeader.setValue("application/json; charset=UTF-8");
// headerManager.add(contentTypeHeader);
// }
// 遍历 headerMap动态添加请求头
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
@@ -409,19 +446,20 @@ public class JMeterGroupUtil {
private static List<ResultCollector> getResultCollector(String replayLogPath, String summaryReportPath) {
// 察看结果数
List<ResultCollector> resultCollectors = new ArrayList<>();
Summariser summariser = new Summariser("速度");
ResultCollector resultCollector = new ResultCollector(summariser);
resultCollector.setProperty(new BooleanProperty("ResultCollector.error_logging", true));
resultCollector.setProperty(new ObjectProperty("saveConfig", getSampleSaveConfig()));
resultCollector.setProperty(new StringProperty("TestElement.gui_class", "org.apache.jmeter.visualizers.ViewResultsFullVisualizer"));
resultCollector.setProperty(new StringProperty("TestElement.name", "察看结果树"));
resultCollector.setProperty(new StringProperty("TestElement.enabled", "true"));
resultCollector.setProperty(new StringProperty("filename", replayLogPath));
resultCollectors.add(resultCollector);
// Summariser summariser = new Summariser("速度");
// ResultCollector resultCollector = new ResultCollector(summariser);
// resultCollector.setProperty(new BooleanProperty("ResultCollector.error_logging", false));
// resultCollector.setProperty(new ObjectProperty("saveConfig", getSampleSaveConfig()));
// resultCollector.setProperty(new StringProperty("TestElement.gui_class", "org.apache.jmeter.visualizers.ViewResultsFullVisualizer"));
// resultCollector.setProperty(new StringProperty("TestElement.name", "察看结果树"));
// resultCollector.setProperty(new StringProperty("TestElement.enabled", "true"));
// resultCollector.setProperty(new StringProperty("filename", replayLogPath));
// resultCollectors.add(resultCollector);
// 结果汇总
ResultCollector resultTotalCollector = new ResultCollector();
resultTotalCollector.setProperty(new BooleanProperty("ResultCollector.error_logging", true));
Summariser summariser = new Summariser("汇总报告");
ResultCollector resultTotalCollector = new ResultCollector(summariser);
resultTotalCollector.setProperty(new BooleanProperty("ResultCollector.error_logging", false));
resultTotalCollector.setProperty(new ObjectProperty("saveConfig", getSampleSaveConfig()));
resultTotalCollector.setProperty(new StringProperty("TestElement.gui_class", "org.apache.jmeter.visualizers.SummaryReport"));
resultTotalCollector.setProperty(new StringProperty("TestElement.name", "汇总报告"));
@@ -450,7 +488,7 @@ public class JMeterGroupUtil {
sampleSaveConfiguration.setEncoding(true);
sampleSaveConfiguration.setAssertions(true);
sampleSaveConfiguration.setSubresults(true);
sampleSaveConfiguration.setResponseData(true);
sampleSaveConfiguration.setResponseData(false);
sampleSaveConfiguration.setSamplerData(true);
sampleSaveConfiguration.setAsXml(true);
sampleSaveConfiguration.setFieldNames(true);
@@ -495,11 +533,12 @@ public class JMeterGroupUtil {
* @param filePath
* @return
*/
private static Map<String, String> getResultMessageFromFile(String filePath) {
private static List<LabelStatsEntity> getResultMessageFromFile(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
return null;
}
List<LabelStatsEntity> statsEntityList = new ArrayList<>();
try {
// 1. 创建DocumentBuilderFactory对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
@@ -509,34 +548,119 @@ public class JMeterGroupUtil {
Document document = builder.parse(new File(filePath));
// 4. 获取所有<httpSample>节点
NodeList httpSampleList = document.getElementsByTagName("httpSample");
Node httpSampleNode = httpSampleList.item(0);
if (httpSampleNode.getNodeType() == Node.ELEMENT_NODE) {
Map<String, String> resultMap = new HashMap<>();
Element httpSampleElement = (Element) httpSampleNode;
String costMiliseconds = httpSampleElement.getAttribute("t");
String responseCode = httpSampleElement.getAttribute("rc");
resultMap.put("responseCode", responseCode);
resultMap.put("costMiliseconds", costMiliseconds);
// 提取子节点<responseData>的内容
NodeList responseDataList = httpSampleElement.getElementsByTagName("responseData");
if (responseDataList.getLength() > 0) {
Element responseDataElement = (Element) responseDataList.item(0);
resultMap.put("responseBody", responseDataElement.getTextContent());
// 初始化统计变量
Map<String, LabelStats> labelStatsMap = new LinkedHashMap<>();
long startTime = Long.MAX_VALUE;
long endTime = Long.MIN_VALUE;
long totalRequests = 0;
long failedRequests = 0;
long totalResponseTime = 0;
long totalMinResponseTime = Long.MAX_VALUE;
long totalMaxResponseTime = Long.MIN_VALUE;
for (int i = 0; i < httpSampleList.getLength(); i++) {
Node httpSampleNode = httpSampleList.item(i);
if (httpSampleNode.getNodeType() == Node.ELEMENT_NODE) {
Map<String, String> resultMap = new HashMap<>();
Element httpSampleElement = (Element) httpSampleNode;
// 提取字段
String label = httpSampleElement.getAttribute("lb");
boolean success = Boolean.parseBoolean(httpSampleElement.getAttribute("s"));
long responseTime = Long.parseLong(httpSampleElement.getAttribute("t"));
long timestamp = Long.parseLong(httpSampleElement.getAttribute("ts"));
long receivedBytes = Long.parseLong(httpSampleElement.getAttribute("by"));
long sentBytes = Long.parseLong(httpSampleElement.getAttribute("sby"));
String responseCode = httpSampleElement.getAttribute("rc");
// 更新全局统计
totalRequests++;
totalResponseTime += responseTime;
totalMinResponseTime = Math.min(totalMinResponseTime, responseTime);
totalMaxResponseTime = Math.max(totalMaxResponseTime, responseTime);
if (!success) {
failedRequests++;
}
startTime = Math.min(startTime, timestamp);
endTime = Math.max(endTime, timestamp);
// 更新按标签的统计
LabelStats stats = labelStatsMap.getOrDefault(label, new LabelStats());
stats.totalRequests++;
stats.totalResponseTime += responseTime;
stats.minResponseTime = Math.min(stats.minResponseTime, responseTime);
stats.maxResponseTime = Math.max(stats.maxResponseTime, responseTime);
stats.receivedBytes += receivedBytes;
stats.sentBytes += sentBytes;
if (!success) {
stats.failedRequests++;
}
labelStatsMap.put(label, stats);
}
// 提取子节点<responseHeader>的内容
NodeList responseHeaderList = httpSampleElement.getElementsByTagName("responseHeader");
if (responseHeaderList.getLength() > 0) {
Element responseHeaderElement = (Element) responseHeaderList.item(0);
resultMap.put("responseHeader", responseHeaderElement.getTextContent());
}
return resultMap;
}
// 计算总时长(秒)
double totalTimeInSeconds = (endTime - startTime) / 1000.0;
// 输出汇总报告
for (Map.Entry<String, LabelStats> entry : labelStatsMap.entrySet()) {
String label = entry.getKey();
LabelStats stats = entry.getValue();
// 计算指标
long average = stats.totalResponseTime / stats.totalRequests;
double errorRate = (stats.failedRequests / (double) stats.totalRequests) * 100;
double throughput = stats.totalRequests / totalTimeInSeconds;
double receivedKBPerSec = (stats.receivedBytes / 1024.0) / totalTimeInSeconds;
double sentKBPerSec = (stats.sentBytes / 1024.0) / totalTimeInSeconds;
LabelStatsEntity labelStatsEntity = new LabelStatsEntity();
labelStatsEntity.setLabel(label);
labelStatsEntity.setSamples(stats.totalRequests);
labelStatsEntity.setAverage(average);
labelStatsEntity.setMin(stats.minResponseTime);
labelStatsEntity.setMax(stats.maxResponseTime);
labelStatsEntity.setErrorRate(errorRate);
labelStatsEntity.setThroughput(throughput);
labelStatsEntity.setReceivedKBPerSec(receivedKBPerSec);
labelStatsEntity.setSentKBPerSec(sentKBPerSec);
statsEntityList.add(labelStatsEntity);
}
// 输出全局汇总
long globalResponseAverage = totalResponseTime / totalRequests;
double globalErrorRate = (failedRequests / (double) totalRequests) * 100;
double globalThroughput = totalRequests / totalTimeInSeconds;
double globalReceivedKBPerSec = labelStatsMap.values().stream()
.mapToLong(stats -> stats.receivedBytes)
.sum() / 1024.0 / totalTimeInSeconds;
double globalSentKBPerSec = labelStatsMap.values().stream()
.mapToLong(stats -> stats.sentBytes)
.sum() / 1024.0 / totalTimeInSeconds;
LabelStatsEntity labelStatsEntity = new LabelStatsEntity();
labelStatsEntity.setLabel("TOTAL");
labelStatsEntity.setSamples(totalRequests);
labelStatsEntity.setAverage(globalResponseAverage);
labelStatsEntity.setMin(totalMinResponseTime);
labelStatsEntity.setMax(totalMaxResponseTime);
labelStatsEntity.setErrorRate(globalErrorRate);
labelStatsEntity.setThroughput(globalThroughput);
labelStatsEntity.setReceivedKBPerSec(globalReceivedKBPerSec);
labelStatsEntity.setSentKBPerSec(globalSentKBPerSec);
statsEntityList.add(labelStatsEntity);
return statsEntityList;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
static class LabelStats {
long totalRequests = 0;
long totalResponseTime = 0;
long minResponseTime = Long.MAX_VALUE;
long maxResponseTime = Long.MIN_VALUE;
int failedRequests = 0;
long receivedBytes = 0;
long sentBytes = 0;
}
/**
* 从响应头中读取Map信息结构
* @param responseHeader

View File

@@ -3,8 +3,6 @@ package com.test.test.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.test.common.annotation.Excel;
import com.test.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.Date;
@@ -35,7 +33,7 @@ public class PerformanceTestCaseReport extends BaseEntity
/** 并发线程数 */
@Excel(name = "并发线程数")
private Long concurrentThreads;
private Integer concurrentThreads;
/** 平均响应时间(毫秒) */
@Excel(name = "平均响应时间", readConverterExp = "毫=秒")
@@ -61,12 +59,16 @@ public class PerformanceTestCaseReport extends BaseEntity
/** 触发方式1-定时任务2-手动 */
@Excel(name = "触发方式1-定时任务2-手动")
private Long triggerType;
private Integer triggerType;
/** 性能测试汇总报告json数组 */
@Excel(name = "性能测试汇总报告json数组")
private String summaryReport;
/** 性能测试错误报告json对象 */
@Excel(name = "性能测试错误报告json对象")
private String errorReport;
/** 申请人姓名 */
@Excel(name = "申请人姓名")
private String applyUser;
@@ -78,164 +80,131 @@ public class PerformanceTestCaseReport extends BaseEntity
/** 删除标记:0正常1删除 */
private String delFlag;
public void setId(Long id)
{
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getId()
{
return id;
public Long getPerformanceId() {
return performanceId;
}
public void setPerformanceId(Long performanceId)
{
public void setPerformanceId(Long performanceId) {
this.performanceId = performanceId;
}
public Long getPerformanceId()
{
return performanceId;
public Long getTestCaseId() {
return testCaseId;
}
public void setTestCaseId(Long testCaseId)
{
public void setTestCaseId(Long testCaseId) {
this.testCaseId = testCaseId;
}
public Long getTestCaseId()
{
return testCaseId;
public Long getSid() {
return sid;
}
public void setSid(Long sid)
{
public void setSid(Long sid) {
this.sid = sid;
}
public Long getSid()
{
return sid;
public Integer getConcurrentThreads() {
return concurrentThreads;
}
public void setConcurrentThreads(Long concurrentThreads)
{
public void setConcurrentThreads(Integer concurrentThreads) {
this.concurrentThreads = concurrentThreads;
}
public Long getConcurrentThreads()
{
return concurrentThreads;
public Long getAverage() {
return average;
}
public void setAverage(Long average)
{
public void setAverage(Long average) {
this.average = average;
}
public Long getAverage()
{
return average;
public String getTps() {
return tps;
}
public void setTps(String tps)
{
public void setTps(String tps) {
this.tps = tps;
}
public String getTps()
{
return tps;
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime)
{
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getStartTime()
{
return startTime;
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime)
{
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public Date getEndTime()
{
return endTime;
public Long getCostTime() {
return costTime;
}
public void setCostTime(Long costTime)
{
public void setCostTime(Long costTime) {
this.costTime = costTime;
}
public Long getCostTime()
{
return costTime;
public Integer getTriggerType() {
return triggerType;
}
public void setTriggerType(Long triggerType)
{
public void setTriggerType(Integer triggerType) {
this.triggerType = triggerType;
}
public Long getTriggerType()
{
return triggerType;
public String getSummaryReport() {
return summaryReport;
}
public void setSummaryReport(String summaryReport)
{
public void setSummaryReport(String summaryReport) {
this.summaryReport = summaryReport;
}
public String getSummaryReport()
{
return summaryReport;
public String getErrorReport() {
return errorReport;
}
public void setApplyUser(String applyUser)
{
public void setErrorReport(String errorReport) {
this.errorReport = errorReport;
}
public String getApplyUser() {
return applyUser;
}
public void setApplyUser(String applyUser) {
this.applyUser = applyUser;
}
public String getApplyUser()
{
return applyUser;
public String getStatus() {
return status;
}
public void setStatus(String status)
{
public void setStatus(String status) {
this.status = status;
}
public String getStatus()
{
return status;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public String getDelFlag()
{
public String getDelFlag() {
return delFlag;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("performanceId", getPerformanceId())
.append("testCaseId", getTestCaseId())
.append("sid", getSid())
.append("concurrentThreads", getConcurrentThreads())
.append("average", getAverage())
.append("tps", getTps())
.append("startTime", getStartTime())
.append("endTime", getEndTime())
.append("costTime", getCostTime())
.append("triggerType", getTriggerType())
.append("summaryReport", getSummaryReport())
.append("applyUser", getApplyUser())
.append("status", getStatus())
.append("delFlag", getDelFlag())
.append("createBy", getCreateBy())
.append("updateBy", getUpdateBy())
.append("createTime", getCreateTime())
.append("updateTime", getUpdateTime())
.toString();
public void setDelFlag(String delFlag) {
this.delFlag = delFlag;
}
}

View File

@@ -12,6 +12,14 @@ import java.util.List;
*/
public interface IPerformanceTestCaseReportService
{
/**
* 执行性能测试并生成报告
* @param id
* @param jmeterHomePath
* @param triggerType 触发方式1-定时任务2-手动
*/
public void executePerformanceTestAndReport(Long id, String jmeterHomePath, Integer triggerType);
/**
* 查询性能测试用例报告
*

View File

@@ -1,13 +1,32 @@
package com.test.test.service.impl;
import com.google.gson.Gson;
import com.test.common.core.domain.model.JmeterGroupRequest;
import com.test.common.core.domain.model.JmeterRequest;
import com.test.common.core.domain.model.LabelStatsEntity;
import com.test.common.core.domain.model.LoginUser;
import com.test.common.utils.DateUtils;
import com.test.common.utils.JMeterGroupUtil;
import com.test.common.utils.SecurityUtils;
import com.test.test.domain.PerformanceTest;
import com.test.test.domain.PerformanceTestCase;
import com.test.test.domain.PerformanceTestCaseReport;
import com.test.test.domain.TestCaseStep;
import com.test.test.mapper.PerformanceTestCaseMapper;
import com.test.test.mapper.PerformanceTestCaseReportMapper;
import com.test.test.mapper.PerformanceTestMapper;
import com.test.test.service.IPerformanceTestCaseReportService;
import com.test.test.service.ITestCaseStepService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* 性能测试用例报告Service业务层处理
@@ -15,11 +34,136 @@ import java.util.List;
* @author test
* @date 2025-04-14
*/
@Slf4j
@Service
public class PerformanceTestCaseReportServiceImpl implements IPerformanceTestCaseReportService
{
@Resource
private PerformanceTestCaseReportMapper performanceTestCaseReportMapper;
@Resource
private PerformanceTestCaseMapper performanceTestCaseMapper;
@Resource
private PerformanceTestMapper performanceTestMapper;
@Autowired
private ITestCaseStepService testCaseStepService;
@Override
public void executePerformanceTestAndReport(Long id, String jmeterHomePath, Integer triggerType) {
Long sid = System.currentTimeMillis();
PerformanceTest performanceTest = performanceTestMapper.selectPerformanceTestById(id);
PerformanceTestCase performanceTestCase = new PerformanceTestCase();
performanceTestCase.setPerformanceId(id);
performanceTestCase.setStatus(1);
List<PerformanceTestCase> relateTestCaseList = performanceTestCaseMapper.selectPerformanceTestCaseList(performanceTestCase);
for (PerformanceTestCase relateTestCase : relateTestCaseList) {
JmeterGroupRequest jmeterGroupRequest = new JmeterGroupRequest();
jmeterGroupRequest.setTestCaseId(relateTestCase.getTestCaseId());
jmeterGroupRequest.setSid(sid);
jmeterGroupRequest.setConcurrentThreads(performanceTest.getConcurrentThreads());
jmeterGroupRequest.setErrorOperType(performanceTest.getErrorOperType());
jmeterGroupRequest.setExecuteType(performanceTest.getExecuteType());
jmeterGroupRequest.setRampUpSeconds(performanceTest.getRampUpSeconds());
Long seconds = performanceTest.getPressureHour() * 3600L + performanceTest.getPressureMinute() * 60L + performanceTest.getPressureSecond() * 1L;
jmeterGroupRequest.setPressureSecond(seconds);
jmeterGroupRequest.setLoopCount(performanceTest.getLoopCount());
jmeterGroupRequest.setRpsStatus(performanceTest.getRpsStatus());
jmeterGroupRequest.setRpsLimit(performanceTest.getRpsLimit());
jmeterGroupRequest.setJmeterHomePath(jmeterHomePath);
List<TestCaseStep> testCaseStepList = testCaseStepService.selectTestCaseStepListByCaseId(relateTestCase.getTestCaseId());
List<JmeterRequest> jmeterRequestList = dealAddTestCaseHttpStep(testCaseStepList);
jmeterGroupRequest.setJmeterRequestList(jmeterRequestList);
Date startTime = new Date();
List<LabelStatsEntity> jmeterResultList = JMeterGroupUtil.getJmeterResult(jmeterGroupRequest);
Date endTime = new Date();
Gson gson = new Gson();
if (!CollectionUtils.isEmpty(jmeterResultList)) {
LabelStatsEntity lastElement = jmeterResultList.get(jmeterResultList.size() - 1);
PerformanceTestCaseReport performanceTestCaseReport = new PerformanceTestCaseReport();
performanceTestCaseReport.setPerformanceId(id);
performanceTestCaseReport.setTestCaseId(relateTestCase.getTestCaseId());
performanceTestCaseReport.setSid(sid);
performanceTestCaseReport.setConcurrentThreads(performanceTest.getConcurrentThreads());
performanceTestCaseReport.setAverage(lastElement.getAverage());
performanceTestCaseReport.setTps(String.format(Locale.US, "%.1f", lastElement.getThroughput()));
performanceTestCaseReport.setStartTime(startTime);
performanceTestCaseReport.setEndTime(endTime);
performanceTestCaseReport.setCostTime(endTime.getTime() - startTime.getTime());
performanceTestCaseReport.setTriggerType(triggerType);
performanceTestCaseReport.setSummaryReport(gson.toJson(jmeterResultList));
performanceTestCaseReport.setErrorReport("");
performanceTestCaseReport.setStatus("1");
LoginUser user = null;
try {
user = SecurityUtils.getLoginUser();
} catch (Exception e) {}
if (user != null) {
performanceTestCaseReport.setCreateBy(user.getUsername());
}
performanceTestCaseReport.setCreateTime(endTime);
performanceTestCaseReportMapper.insertPerformanceTestCaseReport(performanceTestCaseReport);
} else {
log.error(performanceTest.getPerformanceName() + "Jmeter执行性能测试无法获取到汇总报告");
}
}
performanceTest.setStatus("1");
performanceTest.setUpdateTime(new Date());
performanceTestMapper.updatePerformanceTest(performanceTest);
}
/**
* 新增http步骤测试接口到待性能测试列表
* @param testCaseStepList
* @return
*/
private List<JmeterRequest> dealAddTestCaseHttpStep(List<TestCaseStep> testCaseStepList) {
List<JmeterRequest> jmeterRequestList = new ArrayList<>();
for (TestCaseStep testCaseStep : testCaseStepList) {
if (testCaseStep.getType() == 1L) {
// http接口处理
JmeterRequest jmeterRequest = doHttpRequestTestAdd(testCaseStep);
if (jmeterRequest != null) {
jmeterRequestList.add(jmeterRequest);
log.info("用例步骤Http接口:{}新增待性能测试列表成功!", testCaseStep.getName());
}
}
}
return jmeterRequestList;
}
/**
* 处理http接口测试新增
* @param testCaseStep
* @return JmeterRequest
*/
private JmeterRequest doHttpRequestTestAdd(TestCaseStep testCaseStep) {
String url = testCaseStep.getRequestUrl();
String method = testCaseStep.getRequestMethod().toUpperCase();
if (!method.equals("GET") && !method.equals("POST")) {
log.error("不支持的请求方式:{}", method);
return null;
}
if (!url.startsWith("http")) {
String apiProtocol = testCaseStep.getApiProtocol();
String appendUrl = apiProtocol + "://" + testCaseStep.getApiHost();
if (testCaseStep.getApiPort() != null) {
url = appendUrl + ":" + testCaseStep.getApiPort() + url;
} else {
url = appendUrl + url;
}
}
JmeterRequest jmeterRequest = new JmeterRequest();
jmeterRequest.setTestCaseName(testCaseStep.getName());
jmeterRequest.setUrl(url);
jmeterRequest.setPort(testCaseStep.getApiPort());
jmeterRequest.setMethod(testCaseStep.getRequestMethod());
jmeterRequest.setRequestBody(testCaseStep.getRequestBody());
jmeterRequest.setRequestParams(testCaseStep.getRequestParams());
jmeterRequest.setRequestHeader(testCaseStep.getRequestHeader());
log.info("getRequestHeader:{}", jmeterRequest.getRequestHeader());
return jmeterRequest;
}
/**
* 查询性能测试用例报告

View File

@@ -17,6 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="costTime" column="cost_time" />
<result property="triggerType" column="trigger_type" />
<result property="summaryReport" column="summary_report" />
<result property="errorReport" column="error_report" />
<result property="applyUser" column="apply_user" />
<result property="status" column="status" />
<result property="delFlag" column="del_flag" />
@@ -27,7 +28,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectPerformanceTestCaseReportVo">
select id, performance_id, test_case_id, sid, concurrent_threads, average, tps, start_time, end_time, cost_time, trigger_type, summary_report, apply_user, status, del_flag, create_by, update_by, create_time, update_time from performance_test_case_report
select id, performance_id, test_case_id, sid, concurrent_threads, average, tps, start_time, end_time, cost_time, trigger_type, summary_report, error_report, apply_user, status, del_flag, create_by, update_by, create_time, update_time from performance_test_case_report
</sql>
<select id="selectPerformanceTestCaseReportList" parameterType="PerformanceTestCaseReport" resultMap="PerformanceTestCaseReportResult">
@@ -44,6 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="costTime != null "> and cost_time = #{costTime}</if>
<if test="triggerType != null "> and trigger_type = #{triggerType}</if>
<if test="summaryReport != null and summaryReport != ''"> and summary_report = #{summaryReport}</if>
<if test="errorReport != null and errorReport != ''"> and error_report = #{errorReport}</if>
<if test="applyUser != null and applyUser != ''"> and apply_user = #{applyUser}</if>
<if test="status != null and status != ''"> and status = #{status}</if>
</where>
@@ -68,6 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="costTime != null">cost_time,</if>
<if test="triggerType != null">trigger_type,</if>
<if test="summaryReport != null">summary_report,</if>
<if test="errorReport != null">error_report,</if>
<if test="applyUser != null">apply_user,</if>
<if test="status != null">status,</if>
<if test="delFlag != null and delFlag != ''">del_flag,</if>
@@ -88,6 +91,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="costTime != null">#{costTime},</if>
<if test="triggerType != null">#{triggerType},</if>
<if test="summaryReport != null">#{summaryReport},</if>
<if test="errorReport != null">#{errorReport},</if>
<if test="applyUser != null">#{applyUser},</if>
<if test="status != null">#{status},</if>
<if test="delFlag != null and delFlag != ''">#{delFlag},</if>
@@ -112,6 +116,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="costTime != null">cost_time = #{costTime},</if>
<if test="triggerType != null">trigger_type = #{triggerType},</if>
<if test="summaryReport != null">summary_report = #{summaryReport},</if>
<if test="errorReport != null">error_report = #{errorReport},</if>
<if test="applyUser != null">apply_user = #{applyUser},</if>
<if test="status != null">status = #{status},</if>
<if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>