Merge branch 'dev' of https://github.com/fit2cloudrd/metersphere-server into dev
This commit is contained in:
commit
7382c4dcc3
|
@ -110,7 +110,6 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-codec</groupId>
|
<groupId>commons-codec</groupId>
|
||||||
<artifactId>commons-codec</artifactId>
|
<artifactId>commons-codec</artifactId>
|
||||||
<version>1.12</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
|
@ -128,43 +127,12 @@
|
||||||
<artifactId>slf4j-simple</artifactId>
|
<artifactId>slf4j-simple</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- jmeter -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.jmeter</groupId>
|
|
||||||
<artifactId>ApacheJMeter_http</artifactId>
|
|
||||||
<version>${jmeter.version}</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
|
||||||
<artifactId>log4j-slf4j-impl</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<!-- https://mvnrepository.com/artifact/kg.apc/jmeter-plugins-tst -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>kg.apc</groupId>
|
|
||||||
<artifactId>jmeter-plugins-tst</artifactId>
|
|
||||||
<version>2.5</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- https://mvnrepository.com/artifact/kg.apc/jmeter-plugins-casutg -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>kg.apc</groupId>
|
|
||||||
<artifactId>jmeter-plugins-casutg</artifactId>
|
|
||||||
<version>2.9</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.opencsv</groupId>
|
<groupId>com.opencsv</groupId>
|
||||||
<artifactId>opencsv</artifactId>
|
<artifactId>opencsv</artifactId>
|
||||||
<version>5.1</version>
|
<version>5.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
|
||||||
<artifactId>httpclient</artifactId>
|
|
||||||
<version>4.5.3</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -6,8 +6,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
|
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
@SpringBootApplication(exclude = {QuartzAutoConfiguration.class})
|
@SpringBootApplication(exclude = {QuartzAutoConfiguration.class})
|
||||||
@ServletComponentScan
|
@ServletComponentScan
|
||||||
|
@ -17,8 +15,5 @@ public class Application {
|
||||||
SpringApplication.run(Application.class, args);
|
SpringApplication.run(Application.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
public RestTemplate restTemplate() {
|
|
||||||
return new RestTemplate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package io.metersphere.config;
|
package io.metersphere.config;
|
||||||
|
|
||||||
import io.metersphere.interceptor.TestInterceptor;
|
import io.metersphere.interceptor.TestInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
@ -12,4 +14,9 @@ public class WebConfig implements WebMvcConfigurer {
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
registry.addInterceptor(new TestInterceptor());
|
registry.addInterceptor(new TestInterceptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RestTemplate restTemplate() {
|
||||||
|
return new RestTemplate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,9 @@ import io.metersphere.commons.utils.Pager;
|
||||||
import io.metersphere.controller.request.ReportRequest;
|
import io.metersphere.controller.request.ReportRequest;
|
||||||
import io.metersphere.dto.ReportDTO;
|
import io.metersphere.dto.ReportDTO;
|
||||||
import io.metersphere.report.base.Errors;
|
import io.metersphere.report.base.Errors;
|
||||||
import io.metersphere.report.base.RequestStatistics;
|
import io.metersphere.report.base.TestOverview;
|
||||||
import io.metersphere.report.base.RequestStatisticsDTO;
|
import io.metersphere.report.dto.ErrorsTop5DTO;
|
||||||
|
import io.metersphere.report.dto.RequestStatisticsDTO;
|
||||||
import io.metersphere.service.ReportService;
|
import io.metersphere.service.ReportService;
|
||||||
import io.metersphere.user.SessionUtils;
|
import io.metersphere.user.SessionUtils;
|
||||||
import org.apache.shiro.authz.annotation.Logical;
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
|
@ -65,5 +66,15 @@ public class ReportController {
|
||||||
return reportService.getReportErrors(reportId);
|
return reportService.getReportErrors(reportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/content/errors_top5/{reportId}")
|
||||||
|
public ErrorsTop5DTO getReportErrorsTop5(@PathVariable String reportId) {
|
||||||
|
return reportService.getReportErrorsTOP5(reportId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/content/testoverview/{reportId}")
|
||||||
|
public TestOverview getTestOverview(@PathVariable String reportId) {
|
||||||
|
return reportService.getTestOverview(reportId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,12 @@ import io.metersphere.base.domain.TestResource;
|
||||||
import io.metersphere.commons.constants.ResourceStatusEnum;
|
import io.metersphere.commons.constants.ResourceStatusEnum;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||||
import io.metersphere.controller.request.TestRequest;
|
|
||||||
import io.metersphere.dto.NodeDTO;
|
import io.metersphere.dto.NodeDTO;
|
||||||
import io.metersphere.engine.AbstractEngine;
|
import io.metersphere.engine.AbstractEngine;
|
||||||
import io.metersphere.engine.EngineContext;
|
import io.metersphere.engine.EngineContext;
|
||||||
import io.metersphere.engine.EngineFactory;
|
import io.metersphere.engine.EngineFactory;
|
||||||
|
import io.metersphere.engine.docker.request.DockerLoginRequest;
|
||||||
|
import io.metersphere.engine.docker.request.TestRequest;
|
||||||
import io.metersphere.engine.kubernetes.registry.RegistryService;
|
import io.metersphere.engine.kubernetes.registry.RegistryService;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
@ -85,10 +86,12 @@ public class DockerTestEngine extends AbstractEngine {
|
||||||
testRequest.setFileString(content);
|
testRequest.setFileString(content);
|
||||||
testRequest.setImage(registryService.getRegistry() + JMETER_IMAGE);
|
testRequest.setImage(registryService.getRegistry() + JMETER_IMAGE);
|
||||||
testRequest.setTestData(context.getTestData());
|
testRequest.setTestData(context.getTestData());
|
||||||
|
testRequest.setRegistry(registryService.getRegistryUrl());
|
||||||
|
testRequest.setPassword(registryService.getRegistryPassword());
|
||||||
|
testRequest.setUsername(registryService.getRegistryUsername());
|
||||||
// todo 判断测试状态
|
// todo 判断测试状态
|
||||||
String taskStatusUri = String.format(BASE_URL + "/jmeter/task/status/" + testId, nodeIp, port);
|
String taskStatusUri = String.format(BASE_URL + "/jmeter/task/status/" + testId, nodeIp, port);
|
||||||
List containerList = restTemplate.getForObject(taskStatusUri, List.class);
|
List containerList = restTemplate.postForObject(taskStatusUri, testRequest, List.class);
|
||||||
for (int i = 0; i < containerList.size(); i++) {
|
for (int i = 0; i < containerList.size(); i++) {
|
||||||
HashMap h = (HashMap) containerList.get(i);
|
HashMap h = (HashMap) containerList.get(i);
|
||||||
if (StringUtils.equals((String) h.get("State"), "running")) {
|
if (StringUtils.equals((String) h.get("State"), "running")) {
|
||||||
|
@ -103,13 +106,14 @@ public class DockerTestEngine extends AbstractEngine {
|
||||||
public void stop() {
|
public void stop() {
|
||||||
// TODO 停止运行测试
|
// TODO 停止运行测试
|
||||||
String testId = loadTest.getId();
|
String testId = loadTest.getId();
|
||||||
|
DockerLoginRequest request = new DockerLoginRequest();
|
||||||
this.resourceList.forEach(r -> {
|
this.resourceList.forEach(r -> {
|
||||||
NodeDTO node = JSON.parseObject(r.getConfiguration(), NodeDTO.class);
|
NodeDTO node = JSON.parseObject(r.getConfiguration(), NodeDTO.class);
|
||||||
String ip = node.getIp();
|
String ip = node.getIp();
|
||||||
Integer port = node.getPort();
|
Integer port = node.getPort();
|
||||||
|
|
||||||
String uri = String.format(BASE_URL + "/jmeter/container/stop/" + testId, ip, port);
|
String uri = String.format(BASE_URL + "/jmeter/container/stop/" + testId, ip, port);
|
||||||
restTemplate.postForObject(uri, "", String.class);
|
restTemplate.postForObject(uri, request, String.class);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package io.metersphere.engine.docker.request;
|
||||||
|
|
||||||
|
public class DockerLoginRequest {
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private String registry;
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegistry() {
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegistry(String registry) {
|
||||||
|
this.registry = registry;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
package io.metersphere.controller.request;
|
package io.metersphere.engine.docker.request;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class TestRequest {
|
public class TestRequest extends DockerLoginRequest {
|
||||||
|
|
||||||
private int size;
|
private int size;
|
||||||
private String fileString;
|
private String fileString;
|
|
@ -105,15 +105,14 @@ public class KubernetesTestEngine extends AbstractEngine {
|
||||||
public void stop() {
|
public void stop() {
|
||||||
resourceList.forEach(r -> {
|
resourceList.forEach(r -> {
|
||||||
try {
|
try {
|
||||||
EngineContext context = EngineFactory.createContext(loadTest, threadNum);
|
|
||||||
String configuration = r.getConfiguration();
|
String configuration = r.getConfiguration();
|
||||||
ClientCredential clientCredential = JSON.parseObject(configuration, ClientCredential.class);
|
ClientCredential clientCredential = JSON.parseObject(configuration, ClientCredential.class);
|
||||||
KubernetesProvider provider = new KubernetesProvider(JSON.toJSONString(clientCredential));
|
KubernetesProvider provider = new KubernetesProvider(JSON.toJSONString(clientCredential));
|
||||||
provider.confirmNamespace(context.getNamespace());
|
provider.confirmNamespace(loadTest.getProjectId());
|
||||||
Jmeter jmeter = new Jmeter();
|
Jmeter jmeter = new Jmeter();
|
||||||
jmeter.setMetadata(new ObjectMeta() {{
|
jmeter.setMetadata(new ObjectMeta() {{
|
||||||
setName(context.getTestId());
|
setName(loadTest.getId());
|
||||||
setNamespace(context.getNamespace());
|
setNamespace(loadTest.getProjectId());
|
||||||
}});
|
}});
|
||||||
jmeter.setSpec(new JmeterSpec() {{
|
jmeter.setSpec(new JmeterSpec() {{
|
||||||
setReplicas(1);
|
setReplicas(1);
|
||||||
|
|
|
@ -422,7 +422,8 @@ public class JmeterDocumentParser implements DocumentParser {
|
||||||
stringPropCount++;
|
stringPropCount++;
|
||||||
} else {
|
} else {
|
||||||
stringPropCount = 0;
|
stringPropCount = 0;
|
||||||
prop.getFirstChild().setNodeValue(context.getProperty("duration").toString());
|
Integer duration = (Integer) context.getProperty("duration");// 传入的是分钟数, 需要转化成秒数
|
||||||
|
prop.getFirstChild().setNodeValue(String.valueOf(duration * 60));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
prop.getFirstChild().setNodeValue(context.getProperty("rpsLimit").toString());
|
prop.getFirstChild().setNodeValue(context.getProperty("rpsLimit").toString());
|
||||||
|
|
|
@ -3,12 +3,10 @@ package io.metersphere.report;
|
||||||
import com.opencsv.bean.CsvToBean;
|
import com.opencsv.bean.CsvToBean;
|
||||||
import com.opencsv.bean.CsvToBeanBuilder;
|
import com.opencsv.bean.CsvToBeanBuilder;
|
||||||
import com.opencsv.bean.HeaderColumnNameMappingStrategy;
|
import com.opencsv.bean.HeaderColumnNameMappingStrategy;
|
||||||
import io.metersphere.report.base.Errors;
|
import io.metersphere.report.base.*;
|
||||||
import io.metersphere.report.base.Metric;
|
import io.metersphere.report.dto.ErrorsTop5DTO;
|
||||||
import io.metersphere.report.base.RequestStatistics;
|
import io.metersphere.report.dto.RequestStatisticsDTO;
|
||||||
import io.metersphere.report.base.RequestStatisticsDTO;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
@ -21,7 +19,6 @@ public class JtlResolver {
|
||||||
HeaderColumnNameMappingStrategy<Metric> ms = new HeaderColumnNameMappingStrategy<>();
|
HeaderColumnNameMappingStrategy<Metric> ms = new HeaderColumnNameMappingStrategy<>();
|
||||||
ms.setType(Metric.class);
|
ms.setType(Metric.class);
|
||||||
try (Reader reader = new StringReader(jtlString)) {
|
try (Reader reader = new StringReader(jtlString)) {
|
||||||
|
|
||||||
CsvToBean<Metric> cb = new CsvToBeanBuilder<Metric>(reader)
|
CsvToBean<Metric> cb = new CsvToBeanBuilder<Metric>(reader)
|
||||||
.withType(Metric.class)
|
.withType(Metric.class)
|
||||||
.withSkipLines(0)
|
.withSkipLines(0)
|
||||||
|
@ -40,36 +37,33 @@ public class JtlResolver {
|
||||||
List<RequestStatistics> requestStatisticsList = new ArrayList<>();
|
List<RequestStatistics> requestStatisticsList = new ArrayList<>();
|
||||||
Iterator<Map.Entry<String, List<Metric>>> iterator = map.entrySet().iterator();
|
Iterator<Map.Entry<String, List<Metric>>> iterator = map.entrySet().iterator();
|
||||||
List<Integer> allelapse = new ArrayList<>();
|
List<Integer> allelapse = new ArrayList<>();
|
||||||
Integer totalAverage = 0;
|
DecimalFormat df = new DecimalFormat("0.00");
|
||||||
|
int totalAverage = 0;
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Map.Entry<String, List<Metric>> entry = iterator.next();
|
Map.Entry<String, List<Metric>> entry = iterator.next();
|
||||||
String label = entry.getKey();
|
String label = entry.getKey();
|
||||||
List<Metric> list = entry.getValue();
|
List<Metric> list = entry.getValue();
|
||||||
List<String> timestampList = list.stream().map(Metric::getTimestamp).collect(Collectors.toList());
|
List<String> timestampList = list.stream().map(Metric::getTimestamp).collect(Collectors.toList());
|
||||||
int index=0;
|
int index=0;
|
||||||
//总的响应时间
|
|
||||||
int sumElapsed=0;
|
int sumElapsed=0;
|
||||||
Integer failSize = 0;
|
int failSize = 0;
|
||||||
Float totalBytes = 0f;
|
float totalBytes = 0f;
|
||||||
List<Integer> elapsedList = new ArrayList<>();
|
List<Integer> elapsedList = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < list.size(); i++) {
|
for (int i = 0; i < list.size(); i++) {
|
||||||
try {
|
try {
|
||||||
Metric row = list.get(i);
|
Metric row = list.get(i);
|
||||||
//响应时间
|
|
||||||
String elapsed = row.getElapsed();
|
String elapsed = row.getElapsed();
|
||||||
sumElapsed += Integer.valueOf(elapsed);
|
sumElapsed += Integer.parseInt(elapsed);
|
||||||
totalAverage += Integer.valueOf(elapsed);
|
totalAverage += Integer.parseInt(elapsed);
|
||||||
elapsedList.add(Integer.valueOf(elapsed));
|
elapsedList.add(Integer.valueOf(elapsed));
|
||||||
allelapse.add(Integer.valueOf(elapsed));
|
allelapse.add(Integer.valueOf(elapsed));
|
||||||
//成功与否
|
|
||||||
String success = row.getSuccess();
|
String success = row.getSuccess();
|
||||||
if (!"true".equals(success)){
|
if (!"true".equals(success)){
|
||||||
failSize++;
|
failSize++;
|
||||||
}
|
}
|
||||||
//字节
|
|
||||||
String bytes = row.getBytes();
|
String bytes = row.getBytes();
|
||||||
totalBytes += Float.valueOf(bytes);
|
totalBytes += Float.parseFloat(bytes);
|
||||||
index++;
|
index++;
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
System.out.println("exception i:"+i);
|
System.out.println("exception i:"+i);
|
||||||
|
@ -78,17 +72,19 @@ public class JtlResolver {
|
||||||
|
|
||||||
Collections.sort(elapsedList);
|
Collections.sort(elapsedList);
|
||||||
|
|
||||||
Integer tp90 = elapsedList.size()*90/100;
|
int tp90 = elapsedList.size()*90/100;
|
||||||
Integer tp95 = elapsedList.size()*95/100;
|
int tp95 = elapsedList.size()*95/100;
|
||||||
Integer tp99 = elapsedList.size()*99/100;
|
int tp99 = elapsedList.size()*99/100;
|
||||||
Long l = Long.valueOf(timestampList.get(timestampList.size()-1)) - Long.valueOf(timestampList.get(0));
|
long l = Long.valueOf(timestampList.get(timestampList.size()-1)) - Long.valueOf(timestampList.get(0));
|
||||||
|
|
||||||
RequestStatistics requestStatistics = new RequestStatistics();
|
RequestStatistics requestStatistics = new RequestStatistics();
|
||||||
requestStatistics.setRequestLabel(label);
|
requestStatistics.setRequestLabel(label);
|
||||||
requestStatistics.setSamples(index);
|
requestStatistics.setSamples(index);
|
||||||
DecimalFormat df = new DecimalFormat("0.00");
|
|
||||||
|
|
||||||
String s = df.format((float)sumElapsed/index);
|
String s = df.format((float)sumElapsed/index);
|
||||||
requestStatistics.setAverage(s+"");
|
requestStatistics.setAverage(s+"");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TP90的计算
|
* TP90的计算
|
||||||
* 1,把一段时间内全部的请求的响应时间,从小到大排序,获得序列A
|
* 1,把一段时间内全部的请求的响应时间,从小到大排序,获得序列A
|
||||||
|
@ -99,6 +95,7 @@ public class JtlResolver {
|
||||||
requestStatistics.setTp90(elapsedList.get(tp90)+"");
|
requestStatistics.setTp90(elapsedList.get(tp90)+"");
|
||||||
requestStatistics.setTp95(elapsedList.get(tp95)+"");
|
requestStatistics.setTp95(elapsedList.get(tp95)+"");
|
||||||
requestStatistics.setTp99(elapsedList.get(tp99)+"");
|
requestStatistics.setTp99(elapsedList.get(tp99)+"");
|
||||||
|
|
||||||
requestStatistics.setMin(elapsedList.get(0)+"");
|
requestStatistics.setMin(elapsedList.get(0)+"");
|
||||||
requestStatistics.setMax(elapsedList.get(index-1)+"");
|
requestStatistics.setMax(elapsedList.get(index-1)+"");
|
||||||
requestStatistics.setErrors(String.format("%.2f",failSize*100.0/index)+"%");
|
requestStatistics.setErrors(String.format("%.2f",failSize*100.0/index)+"%");
|
||||||
|
@ -112,16 +109,15 @@ public class JtlResolver {
|
||||||
}
|
}
|
||||||
Collections.sort(allelapse);
|
Collections.sort(allelapse);
|
||||||
|
|
||||||
Integer totalTP90 = allelapse.size()*90/100;
|
int totalTP90 = allelapse.size()*90/100;
|
||||||
Integer totalTP95 = allelapse.size()*95/100;
|
int totalTP95 = allelapse.size()*95/100;
|
||||||
Integer totalTP99 = allelapse.size()*99/100;
|
int totalTP99 = allelapse.size()*99/100;
|
||||||
|
|
||||||
Integer min = allelapse.get(0);
|
Integer min = allelapse.get(0);
|
||||||
Integer max = allelapse.get(allelapse.size() - 1);
|
Integer max = allelapse.get(allelapse.size() - 1);
|
||||||
|
|
||||||
Integer allSamples = requestStatisticsList.stream().mapToInt(RequestStatistics::getSamples).sum();
|
int allSamples = requestStatisticsList.stream().mapToInt(RequestStatistics::getSamples).sum();
|
||||||
Integer failSize = requestStatisticsList.stream().mapToInt(RequestStatistics::getKo).sum();
|
int failSize = requestStatisticsList.stream().mapToInt(RequestStatistics::getKo).sum();
|
||||||
DecimalFormat df = new DecimalFormat("0.00");
|
|
||||||
double errors = (double)failSize / allSamples * 100;
|
double errors = (double)failSize / allSamples * 100;
|
||||||
String totalerrors = df.format(errors);
|
String totalerrors = df.format(errors);
|
||||||
double average = (double)totalAverage / allSamples;
|
double average = (double)totalAverage / allSamples;
|
||||||
|
@ -142,20 +138,21 @@ public class JtlResolver {
|
||||||
return statisticsDTO;
|
return statisticsDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate Report
|
// report - Aggregate Report
|
||||||
public static RequestStatisticsDTO getRequestStatistics(String jtlString) {
|
public static RequestStatisticsDTO getRequestStatistics(String jtlString) {
|
||||||
List<Metric> totalLines = resolver(jtlString);
|
List<Metric> totalLines = resolver(jtlString);
|
||||||
Map<String, List<Metric>> map = totalLines.stream().collect(Collectors.groupingBy(Metric::getLabel));
|
Map<String, List<Metric>> map = totalLines.stream().collect(Collectors.groupingBy(Metric::getLabel));
|
||||||
return getOneRpsResult(map);
|
return getOneRpsResult(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errors
|
// report - Errors
|
||||||
public static List<Errors> getErrorsList(String jtlString) {
|
public static List<Errors> getErrorsList(String jtlString) {
|
||||||
List<Metric> totalLines = resolver(jtlString);
|
List<Metric> totalLines = resolver(jtlString);
|
||||||
List<Metric> falseList = totalLines.stream().filter(metric -> StringUtils.equals("false", metric.getSuccess())).collect(Collectors.toList());
|
List<Metric> falseList = totalLines.stream().filter(metric -> StringUtils.equals("false", metric.getSuccess())).collect(Collectors.toList());
|
||||||
List<Errors> errorsList = new ArrayList<>();
|
List<Errors> errorsList = new ArrayList<>();
|
||||||
Map<String, List<Metric>> collect = falseList.stream().collect(Collectors.groupingBy(JtlResolver::getResponseCodeAndFailureMessage));
|
Map<String, List<Metric>> collect = falseList.stream().collect(Collectors.groupingBy(JtlResolver::getResponseCodeAndFailureMessage));
|
||||||
Iterator<Map.Entry<String, List<Metric>>> iterator = collect.entrySet().iterator();
|
Iterator<Map.Entry<String, List<Metric>>> iterator = collect.entrySet().iterator();
|
||||||
|
DecimalFormat df = new DecimalFormat("0.00");
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Map.Entry<String, List<Metric>> next = iterator.next();
|
Map.Entry<String, List<Metric>> next = iterator.next();
|
||||||
String key = next.getKey();
|
String key = next.getKey();
|
||||||
|
@ -163,10 +160,9 @@ public class JtlResolver {
|
||||||
Errors errors = new Errors();
|
Errors errors = new Errors();
|
||||||
errors.setErrorType(key);
|
errors.setErrorType(key);
|
||||||
errors.setErrorNumber(String.valueOf(value.size()));
|
errors.setErrorNumber(String.valueOf(value.size()));
|
||||||
Integer errorSize = value.size();
|
int errorSize = value.size();
|
||||||
Integer errorAllSize = falseList.size();
|
int errorAllSize = falseList.size();
|
||||||
Integer allSamples = totalLines.size();
|
int allSamples = totalLines.size();
|
||||||
DecimalFormat df = new DecimalFormat("0.00");
|
|
||||||
errors.setPrecentOfErrors(df.format((double)errorSize / errorAllSize * 100) + "%");
|
errors.setPrecentOfErrors(df.format((double)errorSize / errorAllSize * 100) + "%");
|
||||||
errors.setPrecentOfAllSamples(df.format((double)errorSize / allSamples * 100) + "%");
|
errors.setPrecentOfAllSamples(df.format((double)errorSize / allSamples * 100) + "%");
|
||||||
errorsList.add(errors);
|
errorsList.add(errors);
|
||||||
|
@ -178,4 +174,106 @@ public class JtlResolver {
|
||||||
return metric.getResponseCode() + "/" + metric.getResponseMessage();
|
return metric.getResponseCode() + "/" + metric.getResponseMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// report - Errors Top 5
|
||||||
|
public static ErrorsTop5DTO getErrorsTop5DTO(String jtlString) {
|
||||||
|
List<Metric> totalLines = resolver(jtlString);
|
||||||
|
ErrorsTop5DTO top5DTO = new ErrorsTop5DTO();
|
||||||
|
List<Metric> falseList = totalLines.stream().filter(metric -> StringUtils.equals("false", metric.getSuccess())).collect(Collectors.toList());
|
||||||
|
Map<String, List<Metric>> collect = falseList.stream().collect(Collectors.groupingBy(JtlResolver::getResponseCodeAndFailureMessage));
|
||||||
|
Iterator<Map.Entry<String, List<Metric>>> iterator = collect.entrySet().iterator();
|
||||||
|
List<ErrorsTop5> errorsTop5s = new ArrayList<>();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Map.Entry<String, List<Metric>> next = iterator.next();
|
||||||
|
String key = next.getKey();
|
||||||
|
List<Metric> value = next.getValue();
|
||||||
|
ErrorsTop5 errorsTop5 = new ErrorsTop5();
|
||||||
|
List<Metric> list = totalLines.stream()
|
||||||
|
.filter(metric -> StringUtils.equals(metric.getLabel(), value.get(0).getLabel())).collect(Collectors.toList());
|
||||||
|
errorsTop5.setSamples(String.valueOf(list.size()));
|
||||||
|
errorsTop5.setSample(value.get(0).getLabel());
|
||||||
|
errorsTop5.setErrors(String.valueOf(value.size()));
|
||||||
|
errorsTop5.setErrorsAllSize(value.size());
|
||||||
|
errorsTop5.setError(key);
|
||||||
|
errorsTop5s.add(errorsTop5);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorsTop5s.sort((t0, t1) -> t1.getErrorsAllSize().compareTo(t0.getErrorsAllSize()));
|
||||||
|
|
||||||
|
if (errorsTop5s.size() >= 5) {
|
||||||
|
errorsTop5s = errorsTop5s.subList(0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
top5DTO.setLabel("Total");
|
||||||
|
top5DTO.setErrorsTop5List(errorsTop5s);
|
||||||
|
top5DTO.setTotalSamples(String.valueOf(totalLines.size()));
|
||||||
|
top5DTO.setTotalErrors(String.valueOf(falseList.size()));
|
||||||
|
int size = errorsTop5s.size();
|
||||||
|
// Total行 信息
|
||||||
|
top5DTO.setError1(size > 0 ? errorsTop5s.get(0).getError() : null);
|
||||||
|
top5DTO.setError1Size(size > 0 ? errorsTop5s.get(0).getErrors() : null);
|
||||||
|
top5DTO.setError2(size > 1 ? errorsTop5s.get(1).getError() : null);
|
||||||
|
top5DTO.setError2Size(size > 1 ? errorsTop5s.get(1).getErrors() : null);
|
||||||
|
top5DTO.setError3(size > 2 ? errorsTop5s.get(2).getError() : null);
|
||||||
|
top5DTO.setError3Size(size > 2 ? errorsTop5s.get(2).getErrors() : null);
|
||||||
|
top5DTO.setError4(size > 3 ? errorsTop5s.get(3).getError() : null);
|
||||||
|
top5DTO.setError4Size(size > 3 ? errorsTop5s.get(3).getErrors() : null);
|
||||||
|
top5DTO.setError5(size > 4 ? errorsTop5s.get(4).getError() : null);
|
||||||
|
top5DTO.setError5Size(size > 4 ? errorsTop5s.get(4).getErrors() : null);
|
||||||
|
|
||||||
|
return top5DTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// report - TestOverview
|
||||||
|
public static TestOverview getTestOverview(String jtlString) {
|
||||||
|
TestOverview testOverview = new TestOverview();
|
||||||
|
List<Metric> total = JtlResolver.resolver(jtlString);
|
||||||
|
Map<String, List<Metric>> collect = total.stream().collect(Collectors.groupingBy(Metric::getTimestamp));
|
||||||
|
Iterator<Map.Entry<String, List<Metric>>> iterator = collect.entrySet().iterator();
|
||||||
|
int max = 0;
|
||||||
|
int totalElapsed = 0;
|
||||||
|
float totalBytes = 0f;
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Map.Entry<String, List<Metric>> entry = iterator.next();
|
||||||
|
List<Metric> list = entry.getValue();
|
||||||
|
if (list.size() > max) {
|
||||||
|
max = list.size();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
Metric metric = list.get(i);
|
||||||
|
String elapsed = metric.getElapsed();
|
||||||
|
totalElapsed += Integer.parseInt(elapsed);
|
||||||
|
String bytes = metric.getBytes();
|
||||||
|
totalBytes += Float.parseFloat(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total.sort(Comparator.comparing(t0 -> Long.valueOf(t0.getTimestamp())));
|
||||||
|
DecimalFormat df = new DecimalFormat("0.00");
|
||||||
|
|
||||||
|
testOverview.setMaxUsers(String.valueOf(max));
|
||||||
|
|
||||||
|
List<Metric> list90 = total.subList(0, total.size() * 9 / 10);
|
||||||
|
long sum = list90.stream().mapToLong(metric -> Long.parseLong(metric.getElapsed())).sum();
|
||||||
|
double avg90 = (double)sum / 1000 / list90.size();
|
||||||
|
testOverview.setResponseTime90(df.format(avg90));
|
||||||
|
|
||||||
|
Long timestamp1 = Long.valueOf(total.get(0).getTimestamp());
|
||||||
|
Long timestamp2 = Long.valueOf(total.get(total.size()-1).getTimestamp());
|
||||||
|
long seconds = (timestamp2 - timestamp1) / 1000;
|
||||||
|
double avgThroughput = (double)total.size() / seconds;
|
||||||
|
testOverview.setAvgThroughput(df.format(avgThroughput));
|
||||||
|
|
||||||
|
List<Metric> falseList = total.stream().filter(metric -> StringUtils.equals("false", metric.getSuccess())).collect(Collectors.toList());
|
||||||
|
double errors = falseList.size() * 1.0 / total.size() * 100;
|
||||||
|
testOverview.setErrors(df.format(errors));
|
||||||
|
|
||||||
|
double avg = totalElapsed * 1.0 / total.size() / 1000; // s
|
||||||
|
testOverview.setAvgResponseTime(df.format(avg));
|
||||||
|
|
||||||
|
double bandwidth = totalBytes * 1.0 / 1024 / seconds;
|
||||||
|
testOverview.setAvgBandwidth(df.format(bandwidth));
|
||||||
|
|
||||||
|
return testOverview;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package io.metersphere.report.base;
|
||||||
|
|
||||||
|
public class ErrorsTop5 {
|
||||||
|
|
||||||
|
private String sample;
|
||||||
|
private String samples;
|
||||||
|
private Integer errorsAllSize;
|
||||||
|
private String error;
|
||||||
|
private String errors;
|
||||||
|
|
||||||
|
public String getSample() {
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSample(String sample) {
|
||||||
|
this.sample = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSamples() {
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSamples(String samples) {
|
||||||
|
this.samples = samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getErrorsAllSize() {
|
||||||
|
return errorsAllSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorsAllSize(Integer errorsAllSize) {
|
||||||
|
this.errorsAllSize = errorsAllSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError(String error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrors() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrors(String errors) {
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package io.metersphere.report.base;
|
||||||
|
|
||||||
|
public class TestOverview {
|
||||||
|
|
||||||
|
private String maxUsers;
|
||||||
|
private String avgThroughput; // Hits/s
|
||||||
|
private String errors;
|
||||||
|
private String avgResponseTime; // s
|
||||||
|
private String responseTime90;
|
||||||
|
private String avgBandwidth;
|
||||||
|
|
||||||
|
public String getMaxUsers() {
|
||||||
|
return maxUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxUsers(String maxUsers) {
|
||||||
|
this.maxUsers = maxUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvgThroughput() {
|
||||||
|
return avgThroughput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvgThroughput(String avgThroughput) {
|
||||||
|
this.avgThroughput = avgThroughput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrors() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrors(String errors) {
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvgResponseTime() {
|
||||||
|
return avgResponseTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvgResponseTime(String avgResponseTime) {
|
||||||
|
this.avgResponseTime = avgResponseTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResponseTime90() {
|
||||||
|
return responseTime90;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResponseTime90(String responseTime90) {
|
||||||
|
this.responseTime90 = responseTime90;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvgBandwidth() {
|
||||||
|
return avgBandwidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvgBandwidth(String avgBandwidth) {
|
||||||
|
this.avgBandwidth = avgBandwidth;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package io.metersphere.report.dto;
|
||||||
|
|
||||||
|
import io.metersphere.report.base.ErrorsTop5;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ErrorsTop5DTO {
|
||||||
|
|
||||||
|
private List<ErrorsTop5> errorsTop5List;
|
||||||
|
private String label;
|
||||||
|
private String totalSamples;
|
||||||
|
private String totalErrors;
|
||||||
|
private String error1;
|
||||||
|
private String error1Size;
|
||||||
|
private String error2;
|
||||||
|
private String error2Size;
|
||||||
|
private String error3;
|
||||||
|
private String error3Size;
|
||||||
|
private String error4;
|
||||||
|
private String error4Size;
|
||||||
|
private String error5;
|
||||||
|
private String error5Size;
|
||||||
|
|
||||||
|
public List<ErrorsTop5> getErrorsTop5List() {
|
||||||
|
return errorsTop5List;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorsTop5List(List<ErrorsTop5> errorsTop5List) {
|
||||||
|
this.errorsTop5List = errorsTop5List;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabel(String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTotalSamples() {
|
||||||
|
return totalSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalSamples(String totalSamples) {
|
||||||
|
this.totalSamples = totalSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTotalErrors() {
|
||||||
|
return totalErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalErrors(String totalErrors) {
|
||||||
|
this.totalErrors = totalErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError1() {
|
||||||
|
return error1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError1(String error1) {
|
||||||
|
this.error1 = error1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError1Size() {
|
||||||
|
return error1Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError1Size(String error1Size) {
|
||||||
|
this.error1Size = error1Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError2() {
|
||||||
|
return error2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError2(String error2) {
|
||||||
|
this.error2 = error2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError2Size() {
|
||||||
|
return error2Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError2Size(String error2Size) {
|
||||||
|
this.error2Size = error2Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError3() {
|
||||||
|
return error3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError3(String error3) {
|
||||||
|
this.error3 = error3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError3Size() {
|
||||||
|
return error3Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError3Size(String error3Size) {
|
||||||
|
this.error3Size = error3Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError4() {
|
||||||
|
return error4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError4(String error4) {
|
||||||
|
this.error4 = error4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError4Size() {
|
||||||
|
return error4Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError4Size(String error4Size) {
|
||||||
|
this.error4Size = error4Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError5() {
|
||||||
|
return error5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError5(String error5) {
|
||||||
|
this.error5 = error5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError5Size() {
|
||||||
|
return error5Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError5Size(String error5Size) {
|
||||||
|
this.error5Size = error5Size;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
package io.metersphere.report.base;
|
package io.metersphere.report.dto;
|
||||||
|
|
||||||
|
import io.metersphere.report.base.RequestStatistics;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -166,6 +166,9 @@ public class LoadTestService {
|
||||||
if (loadTest == null) {
|
if (loadTest == null) {
|
||||||
MSException.throwException(Translator.get("run_load_test_not_found") + request.getId());
|
MSException.throwException(Translator.get("run_load_test_not_found") + request.getId());
|
||||||
}
|
}
|
||||||
|
if (TestStatus.Running.name().equals(loadTest.getStatus())) {
|
||||||
|
MSException.throwException(Translator.get("load_test_is_running") + request.getId());
|
||||||
|
}
|
||||||
|
|
||||||
LogUtil.info("Load test started " + loadTest.getName());
|
LogUtil.info("Load test started " + loadTest.getName());
|
||||||
// engine type (NODE|K8S)
|
// engine type (NODE|K8S)
|
||||||
|
|
|
@ -8,8 +8,9 @@ import io.metersphere.controller.request.ReportRequest;
|
||||||
import io.metersphere.dto.ReportDTO;
|
import io.metersphere.dto.ReportDTO;
|
||||||
import io.metersphere.report.JtlResolver;
|
import io.metersphere.report.JtlResolver;
|
||||||
import io.metersphere.report.base.Errors;
|
import io.metersphere.report.base.Errors;
|
||||||
import io.metersphere.report.base.RequestStatistics;
|
import io.metersphere.report.base.TestOverview;
|
||||||
import io.metersphere.report.base.RequestStatisticsDTO;
|
import io.metersphere.report.dto.ErrorsTop5DTO;
|
||||||
|
import io.metersphere.report.dto.RequestStatisticsDTO;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -59,4 +60,18 @@ public class ReportService {
|
||||||
List<Errors> errors = JtlResolver.getErrorsList(content);
|
List<Errors> errors = JtlResolver.getErrorsList(content);
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ErrorsTop5DTO getReportErrorsTOP5(String id) {
|
||||||
|
LoadTestReport loadTestReport = loadTestReportMapper.selectByPrimaryKey(id);
|
||||||
|
String content = loadTestReport.getContent();
|
||||||
|
ErrorsTop5DTO errors = JtlResolver.getErrorsTop5DTO(content);
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestOverview getTestOverview(String id) {
|
||||||
|
LoadTestReport loadTestReport = loadTestReportMapper.selectByPrimaryKey(id);
|
||||||
|
String content = loadTestReport.getContent();
|
||||||
|
TestOverview testOverview = JtlResolver.getTestOverview(content);
|
||||||
|
return testOverview;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import io.metersphere.dto.TestResourcePoolDTO;
|
||||||
import io.metersphere.engine.kubernetes.provider.KubernetesProvider;
|
import io.metersphere.engine.kubernetes.provider.KubernetesProvider;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
@ -43,6 +43,8 @@ public class TestResourcePoolService {
|
||||||
private TestResourceMapper testResourceMapper;
|
private TestResourceMapper testResourceMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ExtTestReourcePoolMapper extTestReourcePoolMapper;
|
private ExtTestReourcePoolMapper extTestReourcePoolMapper;
|
||||||
|
@Resource
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
|
||||||
public TestResourcePoolDTO addTestResourcePool(TestResourcePoolDTO testResourcePool) {
|
public TestResourcePoolDTO addTestResourcePool(TestResourcePoolDTO testResourcePool) {
|
||||||
testResourcePool.setId(UUID.randomUUID().toString());
|
testResourcePool.setId(UUID.randomUUID().toString());
|
||||||
|
@ -100,9 +102,8 @@ public class TestResourcePoolService {
|
||||||
|
|
||||||
private boolean validateNode(NodeDTO node) {
|
private boolean validateNode(NodeDTO node) {
|
||||||
try {
|
try {
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
|
||||||
ResponseEntity<String> entity = restTemplate.getForEntity(String.format(nodeControllerUrl, node.getIp(), node.getPort()), String.class);
|
ResponseEntity<String> entity = restTemplate.getForEntity(String.format(nodeControllerUrl, node.getIp(), node.getPort()), String.class);
|
||||||
return entity.getStatusCode().value() == HttpStatus.SC_OK;
|
return HttpStatus.OK.equals(entity.getStatusCode());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,5 +12,6 @@
|
||||||
"run_load_test_not_found": "Cannot run test, test not found:",
|
"run_load_test_not_found": "Cannot run test, test not found:",
|
||||||
"run_load_test_file_not_found": "Unable to run test, unable to get test file meta information, test ID:",
|
"run_load_test_file_not_found": "Unable to run test, unable to get test file meta information, test ID:",
|
||||||
"run_load_test_file_content_not_found": "Cannot run test, cannot get test file content, test ID:",
|
"run_load_test_file_content_not_found": "Cannot run test, cannot get test file content, test ID:",
|
||||||
"run_load_test_file_init_error": "Failed to run test, failed to initialize run environment, test ID:"
|
"run_load_test_file_init_error": "Failed to run test, failed to initialize run environment, test ID:",
|
||||||
|
"load_test_is_running": "Load test is running, please wait."
|
||||||
}
|
}
|
|
@ -12,5 +12,6 @@
|
||||||
"run_load_test_not_found": "无法运行测试,未找到测试:",
|
"run_load_test_not_found": "无法运行测试,未找到测试:",
|
||||||
"run_load_test_file_not_found": "无法运行测试,无法获取测试文件元信息,测试ID:",
|
"run_load_test_file_not_found": "无法运行测试,无法获取测试文件元信息,测试ID:",
|
||||||
"run_load_test_file_content_not_found": "无法运行测试,无法获取测试文件内容,测试ID:",
|
"run_load_test_file_content_not_found": "无法运行测试,无法获取测试文件内容,测试ID:",
|
||||||
"run_load_test_file_init_error": "无法运行测试,初始化运行环境失败,测试ID:"
|
"run_load_test_file_init_error": "无法运行测试,初始化运行环境失败,测试ID:",
|
||||||
|
"load_test_is_running": "测试正在运行, 请等待"
|
||||||
}
|
}
|
|
@ -82,6 +82,10 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (to.name !== 'editPerTest') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let testId = to.path.split('/')[4]; // find testId
|
let testId = to.path.split('/')[4]; // find testId
|
||||||
if (testId) {
|
if (testId) {
|
||||||
this.result = this.$get('/testplan/get/' + testId, response => {
|
this.result = this.$get('/testplan/get/' + testId, response => {
|
||||||
|
|
|
@ -218,8 +218,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'$route'(to, from) {
|
'$route'(to) {
|
||||||
if (from.name !== 'createPerTest' && from.name !== 'editPerTest') {
|
if (to.name !== 'createPerTest' && to.name !== 'editPerTest') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let testId = to.path.split('/')[4];
|
let testId = to.path.split('/')[4];
|
||||||
|
|
|
@ -131,8 +131,8 @@
|
||||||
this.getResourcePools();
|
this.getResourcePools();
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'$route'(to, from) {
|
'$route'(to) {
|
||||||
if (from.name !== 'createPerTest' && from.name !== 'editPerTest') {
|
if (to.name !== 'createPerTest' && to.name !== 'editPerTest') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let testId = to.path.split('/')[4];
|
let testId = to.path.split('/')[4];
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
<el-tabs v-model="active" type="border-card" :stretch="true">
|
<el-tabs v-model="active" type="border-card" :stretch="true">
|
||||||
<el-tab-pane :label="$t('report.test_overview')">
|
<el-tab-pane :label="$t('report.test_overview')">
|
||||||
<ms-report-test-overview />
|
<ms-report-test-overview :id="reportId"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="$t('report.test_request_statistics')">
|
<el-tab-pane :label="$t('report.test_request_statistics')">
|
||||||
<ms-report-request-statistics :id="reportId"/>
|
<ms-report-request-statistics :id="reportId"/>
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
<ms-report-error-log :id="reportId"/>
|
<ms-report-error-log :id="reportId"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="$t('report.test_log_details')">
|
<el-tab-pane :label="$t('report.test_log_details')">
|
||||||
<ms-report-log-details />
|
<ms-report-log-details :id="reportId"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|
||||||
|
|
|
@ -34,37 +34,39 @@
|
||||||
|
|
||||||
<span class="table-title">Top 5 Errors by sampler </span>
|
<span class="table-title">Top 5 Errors by sampler </span>
|
||||||
<el-table
|
<el-table
|
||||||
:data="tableData"
|
:data="errorTop5"
|
||||||
border
|
border
|
||||||
stripe
|
stripe
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
|
show-summary
|
||||||
|
:summary-method="getSummaries"
|
||||||
>
|
>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="errorType"
|
prop="sample"
|
||||||
label="Sample"
|
label="Sample"
|
||||||
width="400"
|
width="400"
|
||||||
>
|
>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="errorNumber"
|
prop="samples"
|
||||||
label="#Samples"
|
label="#Samples"
|
||||||
width="120"
|
width="120"
|
||||||
>
|
>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="#Errors"
|
prop="errorsAllSize"
|
||||||
label="#Errors"
|
label="#Errors"
|
||||||
width="100"
|
width="100"
|
||||||
>
|
>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="Error"
|
prop="error"
|
||||||
label="Error"
|
label="Error"
|
||||||
width="400"
|
width="400"
|
||||||
>
|
>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="#Errors"
|
prop="errors"
|
||||||
label="#Errors"
|
label="#Errors"
|
||||||
width="100"
|
width="100"
|
||||||
>
|
>
|
||||||
|
@ -126,7 +128,9 @@
|
||||||
name: "ErrorLog",
|
name: "ErrorLog",
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tableData: [{},{},{},{},{}]
|
tableData: [{},{},{},{},{}],
|
||||||
|
errorTotal: {},
|
||||||
|
errorTop5: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -134,10 +138,32 @@
|
||||||
this.$get("/report/content/errors/" + this.id, res => {
|
this.$get("/report/content/errors/" + this.id, res => {
|
||||||
this.tableData = res.data;
|
this.tableData = res.data;
|
||||||
})
|
})
|
||||||
|
this.$get("/report/content/errors_top5/" + this.id, res => {
|
||||||
|
this.errorTotal = res.data
|
||||||
|
this.errorTop5 = res.data.errorsTop5List;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getSummaries () {
|
||||||
|
const sums = []
|
||||||
|
sums[0] = this.errorTotal.label;
|
||||||
|
sums[1] = this.errorTotal.totalSamples;
|
||||||
|
sums[2] = this.errorTotal.totalErrors;
|
||||||
|
sums[3] = this.errorTotal.error1;
|
||||||
|
sums[4] = this.errorTotal.error1Size;
|
||||||
|
sums[5] = this.errorTotal.error2;
|
||||||
|
sums[6] = this.errorTotal.error2Size;
|
||||||
|
sums[7] = this.errorTotal.error3;
|
||||||
|
sums[8] = this.errorTotal.error3Size;
|
||||||
|
sums[9] = this.errorTotal.error4;
|
||||||
|
sums[10] = this.errorTotal.error4Size;
|
||||||
|
sums[11] = this.errorTotal.error5;
|
||||||
|
sums[12] = this.errorTotal.error5Size;
|
||||||
|
return sums;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.initTableData();
|
this.initTableData();
|
||||||
|
this.getSummaries()
|
||||||
},
|
},
|
||||||
props: ['id'],
|
props: ['id'],
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -145,9 +171,13 @@
|
||||||
if (to.name === "perReportView") {
|
if (to.name === "perReportView") {
|
||||||
let reportId = to.path.split('/')[4];
|
let reportId = to.path.split('/')[4];
|
||||||
if(reportId){
|
if(reportId){
|
||||||
this.$get("/report/content/errors/" + this.id, res => {
|
this.$get("/report/content/errors/" + reportId, res => {
|
||||||
this.tableData = res.data;
|
this.tableData = res.data;
|
||||||
})
|
})
|
||||||
|
this.$get("/report/content/errors_top5/" + reportId, res => {
|
||||||
|
this.errorTop5 = res.data.errorsTop5List;
|
||||||
|
this.errorTotal = res.data
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-card shadow="always" class="ms-card-index-1">
|
<el-card shadow="always" class="ms-card-index-1">
|
||||||
<span class="ms-card-data">
|
<span class="ms-card-data">
|
||||||
<span class="ms-card-data-digital">40</span>
|
<span class="ms-card-data-digital">{{maxUsers}}</span>
|
||||||
<span class="ms-card-data-unit"> VU</span>
|
<span class="ms-card-data-unit"> VU</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="ms-card-desc">Max Users</span>
|
<span class="ms-card-desc">Max Users</span>
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-card shadow="always" class="ms-card-index-2">
|
<el-card shadow="always" class="ms-card-index-2">
|
||||||
<span class="ms-card-data">
|
<span class="ms-card-data">
|
||||||
<span class="ms-card-data-digital">5.4</span>
|
<span class="ms-card-data-digital">{{avgThroughput}}</span>
|
||||||
<span class="ms-card-data-unit"> Hits/s</span>
|
<span class="ms-card-data-unit"> Hits/s</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="ms-card-desc">Avg.Throughput</span>
|
<span class="ms-card-desc">Avg.Throughput</span>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-card shadow="always" class="ms-card-index-3">
|
<el-card shadow="always" class="ms-card-index-3">
|
||||||
<span class="ms-card-data">
|
<span class="ms-card-data">
|
||||||
<span class="ms-card-data-digital">0.41</span>
|
<span class="ms-card-data-digital">{{errors}}</span>
|
||||||
<span class="ms-card-data-unit"> %</span>
|
<span class="ms-card-data-unit"> %</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="ms-card-desc">Errors</span>
|
<span class="ms-card-desc">Errors</span>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-card shadow="always" class="ms-card-index-4">
|
<el-card shadow="always" class="ms-card-index-4">
|
||||||
<span class="ms-card-data">
|
<span class="ms-card-data">
|
||||||
<span class="ms-card-data-digital">1.28</span>
|
<span class="ms-card-data-digital">{{avgResponseTime}}</span>
|
||||||
<span class="ms-card-data-unit"> s</span>
|
<span class="ms-card-data-unit"> s</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="ms-card-desc">Avg.Response Time</span>
|
<span class="ms-card-desc">Avg.Response Time</span>
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-card shadow="always" class="ms-card-index-5">
|
<el-card shadow="always" class="ms-card-index-5">
|
||||||
<span class="ms-card-data">
|
<span class="ms-card-data">
|
||||||
<span class="ms-card-data-digital">1.41</span>
|
<span class="ms-card-data-digital">{{responseTime90}}</span>
|
||||||
<span class="ms-card-data-unit"> s</span>
|
<span class="ms-card-data-unit"> s</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="ms-card-desc">90% Response Time</span>
|
<span class="ms-card-desc">90% Response Time</span>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-card shadow="always" class="ms-card-index-6">
|
<el-card shadow="always" class="ms-card-index-6">
|
||||||
<span class="ms-card-data">
|
<span class="ms-card-data">
|
||||||
<span class="ms-card-data-digital">817.29</span>
|
<span class="ms-card-data-digital">{{avgBandwidth}}</span>
|
||||||
<span class="ms-card-data-unit"> KiB/s</span>
|
<span class="ms-card-data-unit"> KiB/s</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="ms-card-desc">Avg.Bandwidth</span>
|
<span class="ms-card-desc">Avg.Bandwidth</span>
|
||||||
|
@ -73,6 +73,12 @@
|
||||||
name: "TestOverview",
|
name: "TestOverview",
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
maxUsers: "0",
|
||||||
|
avgThroughput: "0",
|
||||||
|
errors: "0",
|
||||||
|
avgResponseTime: "0",
|
||||||
|
responseTime90: "0",
|
||||||
|
avgBandwidth: "0",
|
||||||
option1: {
|
option1: {
|
||||||
legend: {
|
legend: {
|
||||||
top: 20,
|
top: 20,
|
||||||
|
@ -146,6 +152,41 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initTableData() {
|
||||||
|
this.$get("/report/content/testoverview/" + this.id, res => {
|
||||||
|
let data = res.data;
|
||||||
|
this.maxUsers = data.maxUsers;
|
||||||
|
this.avgThroughput = data.avgThroughput;
|
||||||
|
this.errors = data.errors;
|
||||||
|
this.avgResponseTime = data.avgResponseTime;
|
||||||
|
this.responseTime90 = data.responseTime90;
|
||||||
|
this.avgBandwidth = data.avgBandwidth;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.initTableData()
|
||||||
|
},
|
||||||
|
props: ['id'],
|
||||||
|
watch: {
|
||||||
|
'$route'(to) {
|
||||||
|
if (to.name === "perReportView") {
|
||||||
|
let reportId = to.path.split('/')[4];
|
||||||
|
if(reportId){
|
||||||
|
this.$get("/report/content/testoverview/" + reportId, res => {
|
||||||
|
let data = res.data;
|
||||||
|
this.maxUsers = data.maxUsers;
|
||||||
|
this.avgThroughput = data.avgThroughput;
|
||||||
|
this.errors = data.errors;
|
||||||
|
this.avgResponseTime = data.avgResponseTime;
|
||||||
|
this.responseTime90 = data.responseTime90;
|
||||||
|
this.avgBandwidth = data.avgBandwidth;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>2.2.2.RELEASE</version>
|
<version>2.2.6.RELEASE</version>
|
||||||
<relativePath/> <!-- lookup parent from repository -->
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue