This commit is contained in:
chenjianxing 2020-03-29 23:09:56 +08:00
commit 7382c4dcc3
26 changed files with 569 additions and 113 deletions

View File

@ -110,7 +110,6 @@
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
@ -128,43 +127,12 @@
<artifactId>slf4j-simple</artifactId>
</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>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>5.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
</dependencies>
<build>

View File

@ -6,8 +6,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication(exclude = {QuartzAutoConfiguration.class})
@ServletComponentScan
@ -17,8 +15,5 @@ public class Application {
SpringApplication.run(Application.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@ -1,7 +1,9 @@
package io.metersphere.config;
import io.metersphere.interceptor.TestInterceptor;
import org.springframework.context.annotation.Bean;
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.WebMvcConfigurer;
@ -12,4 +14,9 @@ public class WebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor());
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@ -9,8 +9,9 @@ import io.metersphere.commons.utils.Pager;
import io.metersphere.controller.request.ReportRequest;
import io.metersphere.dto.ReportDTO;
import io.metersphere.report.base.Errors;
import io.metersphere.report.base.RequestStatistics;
import io.metersphere.report.base.RequestStatisticsDTO;
import io.metersphere.report.base.TestOverview;
import io.metersphere.report.dto.ErrorsTop5DTO;
import io.metersphere.report.dto.RequestStatisticsDTO;
import io.metersphere.service.ReportService;
import io.metersphere.user.SessionUtils;
import org.apache.shiro.authz.annotation.Logical;
@ -65,5 +66,15 @@ public class ReportController {
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);
}
}

View File

@ -6,11 +6,12 @@ import io.metersphere.base.domain.TestResource;
import io.metersphere.commons.constants.ResourceStatusEnum;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.controller.request.TestRequest;
import io.metersphere.dto.NodeDTO;
import io.metersphere.engine.AbstractEngine;
import io.metersphere.engine.EngineContext;
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 org.apache.commons.lang3.StringUtils;
import org.springframework.web.client.RestTemplate;
@ -85,10 +86,12 @@ public class DockerTestEngine extends AbstractEngine {
testRequest.setFileString(content);
testRequest.setImage(registryService.getRegistry() + JMETER_IMAGE);
testRequest.setTestData(context.getTestData());
testRequest.setRegistry(registryService.getRegistryUrl());
testRequest.setPassword(registryService.getRegistryPassword());
testRequest.setUsername(registryService.getRegistryUsername());
// todo 判断测试状态
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++) {
HashMap h = (HashMap) containerList.get(i);
if (StringUtils.equals((String) h.get("State"), "running")) {
@ -103,13 +106,14 @@ public class DockerTestEngine extends AbstractEngine {
public void stop() {
// TODO 停止运行测试
String testId = loadTest.getId();
DockerLoginRequest request = new DockerLoginRequest();
this.resourceList.forEach(r -> {
NodeDTO node = JSON.parseObject(r.getConfiguration(), NodeDTO.class);
String ip = node.getIp();
Integer port = node.getPort();
String uri = String.format(BASE_URL + "/jmeter/container/stop/" + testId, ip, port);
restTemplate.postForObject(uri, "", String.class);
restTemplate.postForObject(uri, request, String.class);
});

View File

@ -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;
}
}

View File

@ -1,9 +1,9 @@
package io.metersphere.controller.request;
package io.metersphere.engine.docker.request;
import java.util.HashMap;
import java.util.Map;
public class TestRequest {
public class TestRequest extends DockerLoginRequest {
private int size;
private String fileString;

View File

@ -105,15 +105,14 @@ public class KubernetesTestEngine extends AbstractEngine {
public void stop() {
resourceList.forEach(r -> {
try {
EngineContext context = EngineFactory.createContext(loadTest, threadNum);
String configuration = r.getConfiguration();
ClientCredential clientCredential = JSON.parseObject(configuration, ClientCredential.class);
KubernetesProvider provider = new KubernetesProvider(JSON.toJSONString(clientCredential));
provider.confirmNamespace(context.getNamespace());
provider.confirmNamespace(loadTest.getProjectId());
Jmeter jmeter = new Jmeter();
jmeter.setMetadata(new ObjectMeta() {{
setName(context.getTestId());
setNamespace(context.getNamespace());
setName(loadTest.getId());
setNamespace(loadTest.getProjectId());
}});
jmeter.setSpec(new JmeterSpec() {{
setReplicas(1);

View File

@ -422,7 +422,8 @@ public class JmeterDocumentParser implements DocumentParser {
stringPropCount++;
} else {
stringPropCount = 0;
prop.getFirstChild().setNodeValue(context.getProperty("duration").toString());
Integer duration = (Integer) context.getProperty("duration");// 传入的是分钟数, 需要转化成秒数
prop.getFirstChild().setNodeValue(String.valueOf(duration * 60));
continue;
}
prop.getFirstChild().setNodeValue(context.getProperty("rpsLimit").toString());

View File

@ -3,12 +3,10 @@ package io.metersphere.report;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import com.opencsv.bean.HeaderColumnNameMappingStrategy;
import io.metersphere.report.base.Errors;
import io.metersphere.report.base.Metric;
import io.metersphere.report.base.RequestStatistics;
import io.metersphere.report.base.RequestStatisticsDTO;
import io.metersphere.report.base.*;
import io.metersphere.report.dto.ErrorsTop5DTO;
import io.metersphere.report.dto.RequestStatisticsDTO;
import org.apache.commons.lang3.StringUtils;
import java.io.Reader;
import java.io.StringReader;
import java.text.DecimalFormat;
@ -21,7 +19,6 @@ public class JtlResolver {
HeaderColumnNameMappingStrategy<Metric> ms = new HeaderColumnNameMappingStrategy<>();
ms.setType(Metric.class);
try (Reader reader = new StringReader(jtlString)) {
CsvToBean<Metric> cb = new CsvToBeanBuilder<Metric>(reader)
.withType(Metric.class)
.withSkipLines(0)
@ -40,36 +37,33 @@ public class JtlResolver {
List<RequestStatistics> requestStatisticsList = new ArrayList<>();
Iterator<Map.Entry<String, List<Metric>>> iterator = map.entrySet().iterator();
List<Integer> allelapse = new ArrayList<>();
Integer totalAverage = 0;
DecimalFormat df = new DecimalFormat("0.00");
int totalAverage = 0;
while (iterator.hasNext()) {
Map.Entry<String, List<Metric>> entry = iterator.next();
String label = entry.getKey();
List<Metric> list = entry.getValue();
List<String> timestampList = list.stream().map(Metric::getTimestamp).collect(Collectors.toList());
int index=0;
//总的响应时间
int sumElapsed=0;
Integer failSize = 0;
Float totalBytes = 0f;
int failSize = 0;
float totalBytes = 0f;
List<Integer> elapsedList = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
try {
Metric row = list.get(i);
//响应时间
String elapsed = row.getElapsed();
sumElapsed += Integer.valueOf(elapsed);
totalAverage += Integer.valueOf(elapsed);
sumElapsed += Integer.parseInt(elapsed);
totalAverage += Integer.parseInt(elapsed);
elapsedList.add(Integer.valueOf(elapsed));
allelapse.add(Integer.valueOf(elapsed));
//成功与否
String success = row.getSuccess();
if (!"true".equals(success)){
failSize++;
}
//字节
String bytes = row.getBytes();
totalBytes += Float.valueOf(bytes);
totalBytes += Float.parseFloat(bytes);
index++;
}catch (Exception e){
System.out.println("exception i:"+i);
@ -78,17 +72,19 @@ public class JtlResolver {
Collections.sort(elapsedList);
Integer tp90 = elapsedList.size()*90/100;
Integer tp95 = elapsedList.size()*95/100;
Integer tp99 = elapsedList.size()*99/100;
Long l = Long.valueOf(timestampList.get(timestampList.size()-1)) - Long.valueOf(timestampList.get(0));
int tp90 = elapsedList.size()*90/100;
int tp95 = elapsedList.size()*95/100;
int tp99 = elapsedList.size()*99/100;
long l = Long.valueOf(timestampList.get(timestampList.size()-1)) - Long.valueOf(timestampList.get(0));
RequestStatistics requestStatistics = new RequestStatistics();
requestStatistics.setRequestLabel(label);
requestStatistics.setSamples(index);
DecimalFormat df = new DecimalFormat("0.00");
String s = df.format((float)sumElapsed/index);
requestStatistics.setAverage(s+"");
/**
* TP90的计算
* 1把一段时间内全部的请求的响应时间从小到大排序获得序列A
@ -99,6 +95,7 @@ public class JtlResolver {
requestStatistics.setTp90(elapsedList.get(tp90)+"");
requestStatistics.setTp95(elapsedList.get(tp95)+"");
requestStatistics.setTp99(elapsedList.get(tp99)+"");
requestStatistics.setMin(elapsedList.get(0)+"");
requestStatistics.setMax(elapsedList.get(index-1)+"");
requestStatistics.setErrors(String.format("%.2f",failSize*100.0/index)+"%");
@ -112,16 +109,15 @@ public class JtlResolver {
}
Collections.sort(allelapse);
Integer totalTP90 = allelapse.size()*90/100;
Integer totalTP95 = allelapse.size()*95/100;
Integer totalTP99 = allelapse.size()*99/100;
int totalTP90 = allelapse.size()*90/100;
int totalTP95 = allelapse.size()*95/100;
int totalTP99 = allelapse.size()*99/100;
Integer min = allelapse.get(0);
Integer max = allelapse.get(allelapse.size() - 1);
Integer allSamples = requestStatisticsList.stream().mapToInt(RequestStatistics::getSamples).sum();
Integer failSize = requestStatisticsList.stream().mapToInt(RequestStatistics::getKo).sum();
DecimalFormat df = new DecimalFormat("0.00");
int allSamples = requestStatisticsList.stream().mapToInt(RequestStatistics::getSamples).sum();
int failSize = requestStatisticsList.stream().mapToInt(RequestStatistics::getKo).sum();
double errors = (double)failSize / allSamples * 100;
String totalerrors = df.format(errors);
double average = (double)totalAverage / allSamples;
@ -142,20 +138,21 @@ public class JtlResolver {
return statisticsDTO;
}
// Aggregate Report
// report - Aggregate Report
public static RequestStatisticsDTO getRequestStatistics(String jtlString) {
List<Metric> totalLines = resolver(jtlString);
Map<String, List<Metric>> map = totalLines.stream().collect(Collectors.groupingBy(Metric::getLabel));
return getOneRpsResult(map);
}
// Errors
// report - Errors
public static List<Errors> getErrorsList(String jtlString) {
List<Metric> totalLines = resolver(jtlString);
List<Metric> falseList = totalLines.stream().filter(metric -> StringUtils.equals("false", metric.getSuccess())).collect(Collectors.toList());
List<Errors> errorsList = new ArrayList<>();
Map<String, List<Metric>> collect = falseList.stream().collect(Collectors.groupingBy(JtlResolver::getResponseCodeAndFailureMessage));
Iterator<Map.Entry<String, List<Metric>>> iterator = collect.entrySet().iterator();
DecimalFormat df = new DecimalFormat("0.00");
while (iterator.hasNext()) {
Map.Entry<String, List<Metric>> next = iterator.next();
String key = next.getKey();
@ -163,10 +160,9 @@ public class JtlResolver {
Errors errors = new Errors();
errors.setErrorType(key);
errors.setErrorNumber(String.valueOf(value.size()));
Integer errorSize = value.size();
Integer errorAllSize = falseList.size();
Integer allSamples = totalLines.size();
DecimalFormat df = new DecimalFormat("0.00");
int errorSize = value.size();
int errorAllSize = falseList.size();
int allSamples = totalLines.size();
errors.setPrecentOfErrors(df.format((double)errorSize / errorAllSize * 100) + "%");
errors.setPrecentOfAllSamples(df.format((double)errorSize / allSamples * 100) + "%");
errorsList.add(errors);
@ -178,4 +174,106 @@ public class JtlResolver {
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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -1,4 +1,6 @@
package io.metersphere.report.base;
package io.metersphere.report.dto;
import io.metersphere.report.base.RequestStatistics;
import java.util.List;

View File

@ -166,6 +166,9 @@ public class LoadTestService {
if (loadTest == null) {
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());
// engine type (NODE|K8S)

View File

@ -8,8 +8,9 @@ import io.metersphere.controller.request.ReportRequest;
import io.metersphere.dto.ReportDTO;
import io.metersphere.report.JtlResolver;
import io.metersphere.report.base.Errors;
import io.metersphere.report.base.RequestStatistics;
import io.metersphere.report.base.RequestStatisticsDTO;
import io.metersphere.report.base.TestOverview;
import io.metersphere.report.dto.ErrorsTop5DTO;
import io.metersphere.report.dto.RequestStatisticsDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -59,4 +60,18 @@ public class ReportService {
List<Errors> errors = JtlResolver.getErrorsList(content);
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;
}
}

View File

@ -16,7 +16,7 @@ import io.metersphere.dto.TestResourcePoolDTO;
import io.metersphere.engine.kubernetes.provider.KubernetesProvider;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -43,6 +43,8 @@ public class TestResourcePoolService {
private TestResourceMapper testResourceMapper;
@Resource
private ExtTestReourcePoolMapper extTestReourcePoolMapper;
@Resource
private RestTemplate restTemplate;
public TestResourcePoolDTO addTestResourcePool(TestResourcePoolDTO testResourcePool) {
testResourcePool.setId(UUID.randomUUID().toString());
@ -100,9 +102,8 @@ public class TestResourcePoolService {
private boolean validateNode(NodeDTO node) {
try {
RestTemplate restTemplate = new RestTemplate();
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) {
return false;
}

View File

@ -12,5 +12,6 @@
"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_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."
}

View File

@ -12,5 +12,6 @@
"run_load_test_not_found": "无法运行测试,未找到测试:",
"run_load_test_file_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": "测试正在运行, 请等待"
}

View File

@ -82,6 +82,10 @@
return;
}
if (to.name !== 'editPerTest') {
return;
}
let testId = to.path.split('/')[4]; // find testId
if (testId) {
this.result = this.$get('/testplan/get/' + testId, response => {

View File

@ -218,8 +218,8 @@
}
},
watch: {
'$route'(to, from) {
if (from.name !== 'createPerTest' && from.name !== 'editPerTest') {
'$route'(to) {
if (to.name !== 'createPerTest' && to.name !== 'editPerTest') {
return;
}
let testId = to.path.split('/')[4];

View File

@ -131,8 +131,8 @@
this.getResourcePools();
},
watch: {
'$route'(to, from) {
if (from.name !== 'createPerTest' && from.name !== 'editPerTest') {
'$route'(to) {
if (to.name !== 'createPerTest' && to.name !== 'editPerTest') {
return;
}
let testId = to.path.split('/')[4];

View File

@ -35,7 +35,7 @@
<el-tabs v-model="active" type="border-card" :stretch="true">
<el-tab-pane :label="$t('report.test_overview')">
<ms-report-test-overview />
<ms-report-test-overview :id="reportId"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_request_statistics')">
<ms-report-request-statistics :id="reportId"/>
@ -44,7 +44,7 @@
<ms-report-error-log :id="reportId"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_log_details')">
<ms-report-log-details />
<ms-report-log-details :id="reportId"/>
</el-tab-pane>
</el-tabs>

View File

@ -34,37 +34,39 @@
<span class="table-title">Top 5 Errors by sampler </span>
<el-table
:data="tableData"
:data="errorTop5"
border
stripe
style="width: 100%"
show-summary
:summary-method="getSummaries"
>
<el-table-column
prop="errorType"
prop="sample"
label="Sample"
width="400"
>
</el-table-column>
<el-table-column
prop="errorNumber"
prop="samples"
label="#Samples"
width="120"
>
</el-table-column>
<el-table-column
prop="#Errors"
prop="errorsAllSize"
label="#Errors"
width="100"
>
</el-table-column>
<el-table-column
prop="Error"
prop="error"
label="Error"
width="400"
>
</el-table-column>
<el-table-column
prop="#Errors"
prop="errors"
label="#Errors"
width="100"
>
@ -126,7 +128,9 @@
name: "ErrorLog",
data() {
return {
tableData: [{},{},{},{},{}]
tableData: [{},{},{},{},{}],
errorTotal: {},
errorTop5: []
}
},
methods: {
@ -134,10 +138,32 @@
this.$get("/report/content/errors/" + this.id, res => {
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() {
this.initTableData();
this.getSummaries()
},
props: ['id'],
watch: {
@ -145,9 +171,13 @@
if (to.name === "perReportView") {
let reportId = to.path.split('/')[4];
if(reportId){
this.$get("/report/content/errors/" + this.id, res => {
this.$get("/report/content/errors/" + reportId, res => {
this.tableData = res.data;
})
this.$get("/report/content/errors_top5/" + reportId, res => {
this.errorTop5 = res.data.errorsTop5List;
this.errorTotal = res.data
})
}
}
}

View File

@ -4,7 +4,7 @@
<el-col :span="4">
<el-card shadow="always" class="ms-card-index-1">
<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>
<span class="ms-card-desc">Max Users</span>
@ -13,7 +13,7 @@
<el-col :span="4">
<el-card shadow="always" class="ms-card-index-2">
<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>
<span class="ms-card-desc">Avg.Throughput</span>
@ -22,7 +22,7 @@
<el-col :span="4">
<el-card shadow="always" class="ms-card-index-3">
<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>
<span class="ms-card-desc">Errors</span>
@ -31,7 +31,7 @@
<el-col :span="4">
<el-card shadow="always" class="ms-card-index-4">
<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>
<span class="ms-card-desc">Avg.Response Time</span>
@ -40,7 +40,7 @@
<el-col :span="4">
<el-card shadow="always" class="ms-card-index-5">
<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>
<span class="ms-card-desc">90% Response Time</span>
@ -49,7 +49,7 @@
<el-col :span="4">
<el-card shadow="always" class="ms-card-index-6">
<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>
<span class="ms-card-desc">Avg.Bandwidth</span>
@ -73,6 +73,12 @@
name: "TestOverview",
data() {
return {
maxUsers: "0",
avgThroughput: "0",
errors: "0",
avgResponseTime: "0",
responseTime90: "0",
avgBandwidth: "0",
option1: {
legend: {
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>

View File

@ -10,7 +10,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>