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

This commit is contained in:
liangdaliang
2025-04-18 10:56:21 +08:00
parent e160bd8866
commit 6d4e38b1fd
13 changed files with 460 additions and 34 deletions

View File

@@ -0,0 +1,36 @@
package com.test.common.core.domain.model;
/**
* @author liangdaliang
* @DescriptionHttp错误编码统计结果
* @date 2025-04-17 19:05
*/
public class ErrorCodeStat {
private final String responseCode;
private final Long count;
private final Double errorRatio; // 占所有错误的比率
private final Double totalRatio; // 占所有请求的比率
public ErrorCodeStat(String responseCode, long count, double errorRatio, double totalRatio) {
this.responseCode = responseCode;
this.count = count;
this.errorRatio = errorRatio;
this.totalRatio = totalRatio;
}
public String getResponseCode() {
return responseCode;
}
public Long getCount() {
return count;
}
public Double getErrorRatio() {
return errorRatio;
}
public Double getTotalRatio() {
return totalRatio;
}
}

View File

@@ -0,0 +1,47 @@
package com.test.common.core.domain.model;
import java.util.List;
/**
* @author liangdaliang
* @Description总体错误信息统计
* @date 2025-04-17 19:50
*/
public class ErrorTotalStats {
/**
* 错误响应状态码统计列表
*/
private List<ErrorCodeStat> errorCodeStatList;
/**
* Top 5错误接口列表
*/
private List<InterfaceErrorStat> top5InterfaceErrorStatList;
/**
* 每个接口的错误码明细列表
*/
private List<InterfaceErrorDetail> interfaceErrorDetailList;
public List<ErrorCodeStat> getErrorCodeStatList() {
return errorCodeStatList;
}
public void setErrorCodeStatList(List<ErrorCodeStat> errorCodeStatList) {
this.errorCodeStatList = errorCodeStatList;
}
public List<InterfaceErrorStat> getTop5InterfaceErrorStatList() {
return top5InterfaceErrorStatList;
}
public void setTop5InterfaceErrorStatList(List<InterfaceErrorStat> top5InterfaceErrorStatList) {
this.top5InterfaceErrorStatList = top5InterfaceErrorStatList;
}
public List<InterfaceErrorDetail> getInterfaceErrorDetailList() {
return interfaceErrorDetailList;
}
public void setInterfaceErrorDetailList(List<InterfaceErrorDetail> interfaceErrorDetailList) {
this.interfaceErrorDetailList = interfaceErrorDetailList;
}
}

View File

@@ -0,0 +1,42 @@
package com.test.common.core.domain.model;
/**
* @author liangdaliang
* @DescriptionHttp采样
* @date 2025-04-17 19:03
*/
public class HttpSample {
private String lb; // 接口名称
private Boolean s; // true为请求成功
private String rc; // 请求响应编码
public HttpSample(String label, Boolean s, String rc) {
this.lb = label;
this.s = s;
this.rc = rc;
}
public String getLb() {
return lb;
}
public void setLb(String lb) {
this.lb = lb;
}
public Boolean getS() {
return s;
}
public void setS(Boolean s) {
this.s = s;
}
public String getRc() {
return rc;
}
public void setRc(String rc) {
this.rc = rc;
}
}

View File

@@ -0,0 +1,27 @@
package com.test.common.core.domain.model;
import java.util.Map;
/**
* @author liangdaliang
* @Description接口错误明细
* @date 2025-04-17 19:18
*/
public class InterfaceErrorDetail {
private String interfaceName;
private Map<String, Long> errorCodeCounts;
public InterfaceErrorDetail(String interfaceName, Map<String, Long> errorCodeCounts) {
this.interfaceName = interfaceName;
this.errorCodeCounts = errorCodeCounts;
}
public String getInterfaceName() {
return interfaceName;
}
public Map<String, Long> getErrorCodeCounts() {
return errorCodeCounts;
}
}

View File

@@ -0,0 +1,36 @@
package com.test.common.core.domain.model;
/**
* @author liangdaliang
* @Description接口错误统计
* @date 2025-04-17 19:08
*/
public class InterfaceErrorStat {
private String interfaceName;
private Long totalSamples;
private Long errorCount;
private Double errorRate;
public InterfaceErrorStat(String interfaceName, long totalSamples, long errorCount) {
this.interfaceName = interfaceName;
this.totalSamples = totalSamples;
this.errorCount = errorCount;
}
public String getInterfaceName() {
return interfaceName;
}
public Long getTotalSamples() {
return totalSamples;
}
public Long getErrorCount() {
return errorCount;
}
public Double getErrorRate() {
errorRate = (totalSamples == 0 ? 0 : (double) errorCount / totalSamples * 100);
return errorRate;
}
}

View File

@@ -0,0 +1,36 @@
package com.test.common.core.domain.model;
import java.util.List;
/**
* @author liangdaliang
* @DescriptionJmeter性能测试总体报告实体类
* @date 2025-04-18 08:55
*/
public class JmeterGlobalStatEntity {
/**
* Jmeter汇总报告实体对象列表
*/
private List<LabelStatsEntity> statsEntityList;
/**
* Jmeter总体错误信息统计
*/
private ErrorTotalStats errorTotalStats;
public List<LabelStatsEntity> getStatsEntityList() {
return statsEntityList;
}
public void setStatsEntityList(List<LabelStatsEntity> statsEntityList) {
this.statsEntityList = statsEntityList;
}
public ErrorTotalStats getErrorTotalStats() {
return errorTotalStats;
}
public void setErrorTotalStats(ErrorTotalStats errorTotalStats) {
this.errorTotalStats = errorTotalStats;
}
}

View File

@@ -0,0 +1,127 @@
package com.test.common.utils;
import com.test.common.core.domain.model.ErrorCodeStat;
import com.test.common.core.domain.model.HttpSample;
import com.test.common.core.domain.model.InterfaceErrorDetail;
import com.test.common.core.domain.model.InterfaceErrorStat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author liangdaliang
* @DescriptionHttp采样分析工具类
* @date 2025-04-17 19:01
*/
public class HttpSampleAnalyzer {
private final List<HttpSample> samples;
private final int totalRequests;
public HttpSampleAnalyzer(List<HttpSample> samples) {
this.samples = samples;
this.totalRequests = samples.size();
}
/**
* 需求1: 错误响应状态码统计
* @return 错误状态码统计列表
*/
public List<ErrorCodeStat> analyzeErrorCodes() {
// 过滤出所有失败的请求
List<HttpSample> errorSamples = samples.stream()
.filter(s -> !s.getS())
.collect(Collectors.toList());
int totalErrors = errorSamples.size();
// 按状态码分组统计
Map<String, Long> codeCountMap = errorSamples.stream()
.collect(Collectors.groupingBy(
HttpSample::getRc,
Collectors.counting()
));
// 转换为包含比率的统计对象
List<ErrorCodeStat> result = new ArrayList<>();
codeCountMap.forEach((code, count) -> {
double errorRatio = totalErrors == 0 ? 0 : (double) count / totalErrors * 100;
double totalRatio = (double) count / totalRequests * 100;
result.add(new ErrorCodeStat(code, count, errorRatio, totalRatio));
});
// 按错误数量降序排序
result.sort((a, b) -> Long.compare(b.getCount(), a.getCount()));
return result;
}
/**
* 需求2: Top 5错误接口统计
* @return Top 5错误接口列表
*/
public List<InterfaceErrorStat> getTop5ErrorInterfaces() {
// 获取所有接口的错误统计
Map<String, InterfaceErrorStat> allInterfaceStats = getAllInterfacesErrorStats();
// 按错误数量降序排序并取前5
return allInterfaceStats.values().stream()
.sorted((a, b) -> Long.compare(b.getErrorCount(), a.getErrorCount()))
.limit(5)
.collect(Collectors.toList());
}
/**
* 需求3: 每个接口的错误码明细
* @return 接口错误明细列表
*/
public List<InterfaceErrorDetail> getInterfaceErrorDetails() {
// 按接口名称分组
Map<String, List<HttpSample>> samplesByInterface = samples.stream()
.collect(Collectors.groupingBy(HttpSample::getLb));
List<InterfaceErrorDetail> result = new ArrayList<>();
samplesByInterface.forEach((interfaceName, interfaceSamples) -> {
// 过滤出该接口的错误请求
List<HttpSample> errorSamples = interfaceSamples.stream()
.filter(s -> !"true".equals(s.getS()))
.collect(Collectors.toList());
// 按错误码统计
Map<String, Long> errorCodeCounts = errorSamples.stream()
.collect(Collectors.groupingBy(
HttpSample::getRc,
Collectors.counting()
));
if (!errorCodeCounts.isEmpty()) {
result.add(new InterfaceErrorDetail(interfaceName, errorCodeCounts));
}
});
return result;
}
// 辅助方法:获取所有接口的错误统计
private Map<String, InterfaceErrorStat> getAllInterfacesErrorStats() {
// 按接口名称分组
Map<String, List<HttpSample>> samplesByInterface = samples.stream()
.collect(Collectors.groupingBy(HttpSample::getLb));
Map<String, InterfaceErrorStat> result = new HashMap<>();
samplesByInterface.forEach((interfaceName, interfaceSamples) -> {
long total = interfaceSamples.size();
long errors = interfaceSamples.stream()
.filter(s -> !"true".equals(s.getS()))
.count();
result.put(interfaceName, new InterfaceErrorStat(interfaceName, total, errors));
});
return result;
}
}

View File

@@ -4,9 +4,7 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
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 com.test.common.core.domain.model.*;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.gui.ArgumentsPanel;
import org.apache.jmeter.control.LoopController;
@@ -64,7 +62,7 @@ public class JMeterGroupUtil {
* @param jmeterGroupRequest
* @return
*/
public static List<LabelStatsEntity> getJmeterResult(JmeterGroupRequest jmeterGroupRequest) {
public static JmeterGlobalStatEntity getJmeterResult(JmeterGroupRequest jmeterGroupRequest) {
Long id = jmeterGroupRequest.getTestCaseId();
Integer concurrentThreads = jmeterGroupRequest.getConcurrentThreads();
Integer loopCount = jmeterGroupRequest.getLoopCount();
@@ -75,7 +73,7 @@ public class JMeterGroupUtil {
Integer rpsStatus = jmeterGroupRequest.getRpsStatus();
Integer rpsLimit = jmeterGroupRequest.getRpsLimit();
String jmeterHomePath = jmeterGroupRequest.getJmeterHomePath();
List<LabelStatsEntity> result = null;
JmeterGlobalStatEntity result = null;
try {
// 1. 初始化 JMeter
JMeterUtils.loadJMeterProperties(jmeterHomePath + "/bin/jmeter.properties");
@@ -533,7 +531,7 @@ public class JMeterGroupUtil {
* @param filePath
* @return
*/
private static List<LabelStatsEntity> getResultMessageFromFile(String filePath) {
private static JmeterGlobalStatEntity getResultMessageFromFile(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
return null;
@@ -550,6 +548,7 @@ public class JMeterGroupUtil {
NodeList httpSampleList = document.getElementsByTagName("httpSample");
// 初始化统计变量
Map<String, LabelStats> labelStatsMap = new LinkedHashMap<>();
List<HttpSample> samples = new ArrayList<>();
long startTime = Long.MAX_VALUE;
long endTime = Long.MIN_VALUE;
long totalRequests = 0;
@@ -570,6 +569,7 @@ public class JMeterGroupUtil {
long receivedBytes = Long.parseLong(httpSampleElement.getAttribute("by"));
long sentBytes = Long.parseLong(httpSampleElement.getAttribute("sby"));
String responseCode = httpSampleElement.getAttribute("rc");
samples.add(new HttpSample(label, success, responseCode));
// 更新全局统计
totalRequests++;
@@ -644,7 +644,21 @@ public class JMeterGroupUtil {
labelStatsEntity.setReceivedKBPerSec(globalReceivedKBPerSec);
labelStatsEntity.setSentKBPerSec(globalSentKBPerSec);
statsEntityList.add(labelStatsEntity);
return statsEntityList;
// 错误报告汇总
HttpSampleAnalyzer analyzer = new HttpSampleAnalyzer(samples);
List<ErrorCodeStat> errorCodeStatList = analyzer.analyzeErrorCodes();
List<InterfaceErrorStat> top5InterfaceErrorStatList = analyzer.getTop5ErrorInterfaces();
List<InterfaceErrorDetail> interfaceErrorDetailList = analyzer.getInterfaceErrorDetails();
ErrorTotalStats errorTotalStats = new ErrorTotalStats();
errorTotalStats.setErrorCodeStatList(errorCodeStatList);
errorTotalStats.setTop5InterfaceErrorStatList(top5InterfaceErrorStatList);
errorTotalStats.setInterfaceErrorDetailList(interfaceErrorDetailList);
JmeterGlobalStatEntity globalStatEntity = new JmeterGlobalStatEntity();
globalStatEntity.setStatsEntityList(statsEntityList);
globalStatEntity.setErrorTotalStats(errorTotalStats);
return globalStatEntity;
} catch (Exception e) {
e.printStackTrace();
}