diff --git a/test-common/src/main/java/com/test/common/core/domain/model/JmeterGroupRequest.java b/test-common/src/main/java/com/test/common/core/domain/model/JmeterGroupRequest.java index cf8532f..2d1aa17 100644 --- a/test-common/src/main/java/com/test/common/core/domain/model/JmeterGroupRequest.java +++ b/test-common/src/main/java/com/test/common/core/domain/model/JmeterGroupRequest.java @@ -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; } diff --git a/test-common/src/main/java/com/test/common/core/domain/model/JmeterRequest.java b/test-common/src/main/java/com/test/common/core/domain/model/JmeterRequest.java index 61a8a9a..98cec0a 100644 --- a/test-common/src/main/java/com/test/common/core/domain/model/JmeterRequest.java +++ b/test-common/src/main/java/com/test/common/core/domain/model/JmeterRequest.java @@ -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; } diff --git a/test-common/src/main/java/com/test/common/core/domain/model/LabelStatsEntity.java b/test-common/src/main/java/com/test/common/core/domain/model/LabelStatsEntity.java new file mode 100644 index 0000000..5092c0c --- /dev/null +++ b/test-common/src/main/java/com/test/common/core/domain/model/LabelStatsEntity.java @@ -0,0 +1,96 @@ +package com.test.common.core.domain.model; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * @author liangdaliang + * @Description:Jmeter汇总报告实体对象 + * @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; + } +} diff --git a/test-common/src/main/java/com/test/common/utils/JMeterGroupUtil.java b/test-common/src/main/java/com/test/common/utils/JMeterGroupUtil.java index f5b0a04..c154ca4 100644 --- a/test-common/src/main/java/com/test/common/utils/JMeterGroupUtil.java +++ b/test-common/src/main/java/com/test/common/utils/JMeterGroupUtil.java @@ -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 getJmeterResult(JmeterGroupRequest jmeterGroupRequest) { + public static List 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 result = null; + List 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 requestList) { + private static void dealHttpHashTreeWithRequestList(ListedHashTree thirdHashTree, List 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 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 requestParams) throws MalformedURLException { + private static HTTPSamplerProxy createGetHttpSampler(String testCaseName, String urlString, int port, Map 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 requestParams) throws MalformedURLException { + private static HTTPSamplerProxy createPostHttpSampler(String testCaseName, String urlString, int port, String requestBody, Map 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 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 entry : headerMap.entrySet()) { @@ -409,19 +446,20 @@ public class JMeterGroupUtil { private static List getResultCollector(String replayLogPath, String summaryReportPath) { // 察看结果数 List 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 getResultMessageFromFile(String filePath) { + private static List getResultMessageFromFile(String filePath) { File file = new File(filePath); if (!file.exists()) { return null; } + List 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. 获取所有节点 NodeList httpSampleList = document.getElementsByTagName("httpSample"); - Node httpSampleNode = httpSampleList.item(0); - if (httpSampleNode.getNodeType() == Node.ELEMENT_NODE) { - Map 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); - // 提取子节点的内容 - NodeList responseDataList = httpSampleElement.getElementsByTagName("responseData"); - if (responseDataList.getLength() > 0) { - Element responseDataElement = (Element) responseDataList.item(0); - resultMap.put("responseBody", responseDataElement.getTextContent()); + // 初始化统计变量 + Map 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 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); } - // 提取子节点的内容 - 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 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 diff --git a/test-test/src/main/java/com/test/test/domain/PerformanceTestCaseReport.java b/test-test/src/main/java/com/test/test/domain/PerformanceTestCaseReport.java index 8de7659..00010cc 100644 --- a/test-test/src/main/java/com/test/test/domain/PerformanceTestCaseReport.java +++ b/test-test/src/main/java/com/test/test/domain/PerformanceTestCaseReport.java @@ -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; } } diff --git a/test-test/src/main/java/com/test/test/service/IPerformanceTestCaseReportService.java b/test-test/src/main/java/com/test/test/service/IPerformanceTestCaseReportService.java index 07d5883..6386bd1 100644 --- a/test-test/src/main/java/com/test/test/service/IPerformanceTestCaseReportService.java +++ b/test-test/src/main/java/com/test/test/service/IPerformanceTestCaseReportService.java @@ -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); + /** * 查询性能测试用例报告 * diff --git a/test-test/src/main/java/com/test/test/service/impl/PerformanceTestCaseReportServiceImpl.java b/test-test/src/main/java/com/test/test/service/impl/PerformanceTestCaseReportServiceImpl.java index 751a271..f599d05 100644 --- a/test-test/src/main/java/com/test/test/service/impl/PerformanceTestCaseReportServiceImpl.java +++ b/test-test/src/main/java/com/test/test/service/impl/PerformanceTestCaseReportServiceImpl.java @@ -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 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 testCaseStepList = testCaseStepService.selectTestCaseStepListByCaseId(relateTestCase.getTestCaseId()); + List jmeterRequestList = dealAddTestCaseHttpStep(testCaseStepList); + jmeterGroupRequest.setJmeterRequestList(jmeterRequestList); + Date startTime = new Date(); + List 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 dealAddTestCaseHttpStep(List testCaseStepList) { + List 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; + } /** * 查询性能测试用例报告 diff --git a/test-test/src/main/resources/mapper/test/PerformanceTestCaseReportMapper.xml b/test-test/src/main/resources/mapper/test/PerformanceTestCaseReportMapper.xml index 2c3f08b..44f363b 100644 --- a/test-test/src/main/resources/mapper/test/PerformanceTestCaseReportMapper.xml +++ b/test-test/src/main/resources/mapper/test/PerformanceTestCaseReportMapper.xml @@ -17,6 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -27,7 +28,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - 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