diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java b/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java new file mode 100644 index 0000000000..30b7c2aca1 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/controller/ApiJmeterFileController.java @@ -0,0 +1,39 @@ +package io.metersphere.api.controller; + +import io.metersphere.api.service.ApiJmeterFileService; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.UUID; + +@RestController +@RequestMapping("/api/jmeter") +public class ApiJmeterFileController { + + @Resource + private ApiJmeterFileService apiJmeterFileService; + + @GetMapping("download") + public ResponseEntity downloadJmeterFiles(@RequestParam("testId") String testId, @RequestParam("reportId") String reportId, @RequestParam("runMode") String runMode, @RequestParam("testPlanScenarioId") String testPlanScenarioId) { + byte[] bytes = apiJmeterFileService.downloadJmeterFiles(runMode,testId, reportId, testPlanScenarioId); + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType("application/octet-stream")) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + testId + ".zip\"") + .body(bytes); + } + + @GetMapping("download/jar") + public ResponseEntity downloadJmeterFiles() { + byte[] bytes = apiJmeterFileService.downloadJmeterJar(); + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType("application/octet-stream")) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + UUID.randomUUID().toString() + ".zip\"") + .body(bytes); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/RunModeDataDTO.java b/backend/src/main/java/io/metersphere/api/dto/RunModeDataDTO.java new file mode 100644 index 0000000000..d45730109d --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/RunModeDataDTO.java @@ -0,0 +1,25 @@ +package io.metersphere.api.dto; + +import lombok.Getter; +import lombok.Setter; +import org.apache.jorphan.collections.HashTree; + +@Getter +@Setter +public class RunModeDataDTO { + // 执行HashTree + private HashTree hashTree; + // 测试场景/测试用例 + private String testId; + // 报告id + private String reportId; + public RunModeDataDTO(String testId,String reportId) { + this.testId = testId; + this.reportId = reportId; + } + + public RunModeDataDTO(HashTree hashTree,String reportId) { + this.hashTree = hashTree; + this.reportId = reportId; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/RunRequest.java b/backend/src/main/java/io/metersphere/api/dto/RunRequest.java index 760ca12fdf..84ac1a342e 100644 --- a/backend/src/main/java/io/metersphere/api/dto/RunRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/RunRequest.java @@ -8,6 +8,7 @@ import lombok.Setter; @Setter public class RunRequest { private String testId; + private String url; private String userId; private boolean isDebug; private String runMode; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/BatchRunDefinitionRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/BatchRunDefinitionRequest.java new file mode 100644 index 0000000000..8045ff6905 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/BatchRunDefinitionRequest.java @@ -0,0 +1,18 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.api.dto.automation.RunModeConfig; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +public class BatchRunDefinitionRequest { + private String id; + + private List planIds; + + private RunModeConfig config; + +} diff --git a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java index a50279afb9..edb700dbb3 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java @@ -40,8 +40,6 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl public final static String TEST_ID = "ms.test.id"; - public final static String TEST_REPORT_ID = "ms.test.report.name"; - private final static String THREAD_SPLIT = " "; private final static String ID_SPLIT = "-"; @@ -76,8 +74,6 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl private String testId; private String debugReportId; - // 只有合并报告是这个有值 - private String setReportId; //获得控制台内容 private PrintStream oldPrintStream = System.out; @@ -156,8 +152,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl TestResult testResult = new TestResult(); testResult.setTestId(testId); testResult.setTotal(queue.size()); - testResult.setSetReportId(this.setReportId); - testResult.setConsole(getConsole()); + testResult.setConsole(getConsole()); // 一个脚本里可能包含多个场景(ThreadGroup),所以要区分开,key: 场景Id final Map scenarios = new LinkedHashMap<>(); @@ -506,7 +501,6 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl private void setParam(BackendListenerContext context) { this.testId = context.getParameter(TEST_ID); - this.setReportId = context.getParameter(TEST_REPORT_ID); this.runMode = context.getParameter("runMode"); this.debugReportId = context.getParameter("debugReportId"); if (StringUtils.isBlank(this.runMode)) { diff --git a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java index d2e5f6632d..5640321fa9 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java @@ -3,13 +3,11 @@ package io.metersphere.api.jmeter; import com.alibaba.fastjson.JSON; import io.metersphere.api.dto.RunRequest; import io.metersphere.api.dto.automation.RunModeConfig; -import io.metersphere.api.dto.definition.request.MsTestPlan; import io.metersphere.api.dto.scenario.request.BodyFile; import io.metersphere.api.service.ApiScenarioReportService; import io.metersphere.base.domain.JarConfig; import io.metersphere.base.domain.TestResource; import io.metersphere.commons.constants.ApiRunMode; -import io.metersphere.commons.constants.RunModeConstants; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.*; import io.metersphere.config.JmeterProperties; @@ -31,13 +29,8 @@ import org.apache.jorphan.collections.HashTree; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.FileSystemResource; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import javax.annotation.PostConstruct; @@ -47,7 +40,10 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.lang.reflect.Field; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; @Service public class JMeterService { @@ -125,10 +121,6 @@ public class JMeterService { BackendListener backendListener = new BackendListener(); backendListener.setName(testId); Arguments arguments = new Arguments(); - if (config != null && config.getMode().equals(RunModeConstants.SERIAL.toString()) && config.getReportType().equals(RunModeConstants.SET_REPORT.toString())) { - arguments.addArgument(APIBackendListenerClient.TEST_REPORT_ID, config.getReportId()); - - } arguments.addArgument(APIBackendListenerClient.TEST_ID, testId); if (StringUtils.isNotBlank(runMode)) { arguments.addArgument("runMode", runMode); @@ -141,6 +133,16 @@ public class JMeterService { testPlan.add(testPlan.getArray()[0], backendListener); } + public void addBackendListener(String testId, HashTree testPlan) { + BackendListener backendListener = new BackendListener(); + backendListener.setName(testId); + Arguments arguments = new Arguments(); + arguments.addArgument(APIBackendListenerClient.TEST_ID, testId); + backendListener.setArguments(arguments); + backendListener.setClassname(APIBackendListenerClient.class.getCanonicalName()); + testPlan.add(testPlan.getArray()[0], backendListener); + } + public void runDefinition(String testId, HashTree testPlan, String debugReportId, String runMode) { try { init(); @@ -198,7 +200,7 @@ public class JMeterService { } } - private byte[] fileToByte(File tradeFile) { + public byte[] fileToByte(File tradeFile) { byte[] buffer = null; try (FileInputStream fis = new FileInputStream(tradeFile); ByteArrayOutputStream bos = new ByteArrayOutputStream();) { @@ -213,7 +215,7 @@ public class JMeterService { return buffer; } - private List getZipJar() { + public List getZipJar() { List jarFiles = new LinkedList<>(); // jar 包 JarConfigService jarConfigService = CommonBeanFactory.getBean(JarConfigService.class); @@ -249,7 +251,7 @@ public class JMeterService { return jarFiles; } - private List getJar() { + public List getJar() { List jarFiles = new LinkedList<>(); // jar 包 JarConfigService jarConfigService = CommonBeanFactory.getBean(JarConfigService.class); @@ -280,7 +282,7 @@ public class JMeterService { return jarFiles; } - private List getMultipartFiles(HashTree hashTree) { + public List getMultipartFiles(HashTree hashTree) { List multipartFiles = new LinkedList<>(); // 获取附件 List files = new LinkedList<>(); @@ -306,12 +308,7 @@ public class JMeterService { return multipartFiles; } - public void runTest(String testId, HashTree hashTree, String runMode, boolean isDebug, RunModeConfig config) { - // 获取JMX使用到的附件 - List multipartFiles = getMultipartFiles(hashTree); - // 获取JAR - List jarFiles = getJar(); - + public void runTest(String testId, String reportId, String runMode, String testPlanScenarioId, RunModeConfig config) { // 获取可以执行的资源池 String resourcePoolId = config.getResourcePoolId(); TestResource testResource = resourcePoolCalculation.getPool(resourcePoolId); @@ -327,54 +324,26 @@ public class JMeterService { if (baseInfo != null) { metersphereUrl = baseInfo.getUrl(); } - // 检查≈地址是否正确 - String jmeterPingUrl = metersphereUrl + "/jmeter/ping"; - // docker 不能从 localhost 中下载文件 - if (StringUtils.contains(metersphereUrl, "http://localhost") - || !UrlTestUtils.testUrlWithTimeOut(jmeterPingUrl, 1000)) { - MSException.throwException(Translator.get("run_load_test_file_init_error")); - } - - String uri = String.format(BASE_URL + "/jmeter/api/run", nodeIp, port); try { - File file = new File(FileUtils.BODY_FILE_DIR + "/tmp"); - if (!file.exists()) { - file.createNewFile(); - } - RunRequest runRequest = new RunRequest(); runRequest.setTestId(testId); - runRequest.setDebug(isDebug); + metersphereUrl += "/api/jmeter/download?testId=" + testId + "&reportId=" + reportId + "&testPlanScenarioId" + "&runMode=" + runMode; + if (StringUtils.isNotEmpty(testPlanScenarioId)) { + metersphereUrl += "=" + testPlanScenarioId; + } + runRequest.setUrl(metersphereUrl); runRequest.setRunMode(runMode); - runRequest.setConfig(config); - runRequest.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); - runRequest.setJmx(new MsTestPlan().getJmx(hashTree)); - MultiValueMap postParameters = new LinkedMultiValueMap<>(); - if (CollectionUtils.isEmpty(multipartFiles)) { - multipartFiles.add(new FileSystemResource(file)); - } - if (CollectionUtils.isEmpty(jarFiles)) { - jarFiles.add(new FileSystemResource(file)); - } - postParameters.put("files", multipartFiles); - postParameters.put("jarFiles", jarFiles); - postParameters.add("request", JSON.toJSONString(runRequest)); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)); - HttpEntity> request = new HttpEntity<>(postParameters, headers); - - ResponseEntity result = restTemplate.postForEntity(uri, request, String.class); + String uri = String.format(BASE_URL + "/jmeter/api/start", nodeIp, port); + ResponseEntity result = restTemplate.postForEntity(uri, runRequest, String.class); if (result == null || !StringUtils.equals("SUCCESS", result.getBody())) { // 清理零时报告 ApiScenarioReportService apiScenarioReportService = CommonBeanFactory.getBean(ApiScenarioReportService.class); - apiScenarioReportService.delete(testId); + apiScenarioReportService.delete(reportId); MSException.throwException("执行失败:" + result); } } catch (Exception e) { e.printStackTrace(); - MSException.throwException("Please check node-controller status."); + MSException.throwException(e.getMessage()); } } } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/TestResult.java b/backend/src/main/java/io/metersphere/api/jmeter/TestResult.java index 060ce66d97..7de3438d16 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/TestResult.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/TestResult.java @@ -16,8 +16,6 @@ public class TestResult { private String testId; - private String setReportId; - private int scenarioTotal; private int scenarioSuccess; diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index 9eb1552251..6bbaf19ae7 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -776,7 +776,7 @@ public class ApiAutomationService { } } - private HashTree generateHashTree(ApiScenarioWithBLOBs item, String reportId, Map planEnvMap) { + public HashTree generateHashTree(ApiScenarioWithBLOBs item, String reportId, Map planEnvMap) { HashTree jmeterHashTree = new HashTree(); MsTestPlan testPlan = new MsTestPlan(); testPlan.setHashTree(new LinkedList<>()); @@ -903,7 +903,7 @@ public class ApiAutomationService { request.setTriggerMode(ReportTriggerMode.MANUAL.name()); } String reportId = request.getId(); - Map map = new LinkedHashMap<>(); + Map map = new LinkedHashMap<>(); List scenarioIds = new ArrayList<>(); StringBuilder scenarioNames = new StringBuilder(); // 按照场景执行 @@ -928,19 +928,23 @@ public class ApiAutomationService { if (request.isTestPlanScheduleJob()) { String savedScenarioId = testPlanScenarioId + ":" + request.getTestPlanReportId(); report = createScenarioReport(reportId, savedScenarioId, item.getName(), request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID(),request.getConfig()); + request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig()); } else { report = createScenarioReport(reportId, testPlanScenarioId, item.getName(), request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID(),request.getConfig()); + request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig()); } } else { report = createScenarioReport(reportId, ExecuteType.Marge.name().equals(request.getExecuteType()) ? serialReportId : item.getId(), item.getName(), request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID(),request.getConfig()); + request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig()); } try { - // 生成报告和HashTree - HashTree hashTree = generateHashTree(item, reportId, planEnvMap); - map.put(report, hashTree); + if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { + map.put(report, new RunModeDataDTO(item.getId(), report.getId())); + } else { + // 生成报告和HashTree + HashTree hashTree = generateHashTree(item, reportId, planEnvMap); + map.put(report, new RunModeDataDTO(hashTree, report.getId())); + } scenarioIds.add(item.getId()); scenarioNames.append(item.getName()).append(","); // 重置报告ID @@ -953,7 +957,7 @@ public class ApiAutomationService { if (request.getConfig() != null && StringUtils.equals(request.getConfig().getReportType(), RunModeConstants.SET_REPORT.toString()) && StringUtils.isNotEmpty(request.getConfig().getReportName())) { request.getConfig().setReportId(UUID.randomUUID().toString()); APIScenarioReportResult report = createScenarioReport(request.getConfig().getReportId(), JSON.toJSONString(scenarioIds), scenarioNames.deleteCharAt(scenarioNames.toString().length() - 1).toString(), ReportTriggerMode.MANUAL.name(), - ExecuteType.Saved.name(), request.getProjectId(), request.getReportUserID(),request.getConfig()); + ExecuteType.Saved.name(), request.getProjectId(), request.getReportUserID(), request.getConfig()); report.setName(request.getConfig().getReportName()); report.setId(serialReportId); apiScenarioReportMapper.insert(report); @@ -974,7 +978,7 @@ public class ApiAutomationService { return request.getId(); } - private void run(Map map, RunScenarioRequest request, String serialReportId) { + private void run(Map map, RunScenarioRequest request, String serialReportId) { // 开始选择执行模式 ExecutorService executorService = Executors.newFixedThreadPool(map.size()); if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { @@ -987,7 +991,7 @@ public class ApiAutomationService { apiScenarioReportMapper.insert(key); reportIds.add(key.getId()); try { - Future future = executorService.submit(new SerialScenarioExecTask(jMeterService, apiScenarioReportMapper, key.getId(), map.get(key), request)); + Future future = executorService.submit(new SerialScenarioExecTask(jMeterService, apiScenarioReportMapper, map.get(key), request)); ApiScenarioReport report = future.get(); // 如果开启失败结束执行,则判断返回结果状态 if (request.getConfig().isOnSampleError()) { @@ -1015,7 +1019,7 @@ public class ApiAutomationService { for (APIScenarioReportResult report : map.keySet()) { //存储报告 batchMapper.insert(report); - executorService.submit(new ParallelScenarioExecTask(jMeterService, report.getId(), map.get(report), request)); + executorService.submit(new ParallelScenarioExecTask(jMeterService, map.get(report), request)); } sqlSession.flushStatements(); } @@ -1095,14 +1099,14 @@ public class ApiAutomationService { if (request.isTestPlanScheduleJob()) { String savedScenarioId = testPlanScenarioId + ":" + request.getTestPlanReportId(); report = createScenarioReport(group.getName(), savedScenarioId, item.getName(), request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID(),request.getConfig()); + request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig()); } else { report = createScenarioReport(group.getName(), testPlanScenarioId, item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID(),request.getConfig()); + request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig()); } } else { report = createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), - request.getExecuteType(), item.getProjectId(), request.getReportUserID(),request.getConfig()); + request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig()); } batchMapper.insert(report); reportIds.add(group.getName()); @@ -1223,12 +1227,7 @@ public class ApiAutomationService { List reportIds = new LinkedList<>(); try { HashTree hashTree = generateHashTree(apiScenarios, request, reportIds); - if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { - jMeterService.runTest(JSON.toJSONString(reportIds), hashTree, runMode, false, request.getConfig()); - } else { - jMeterService.runSerial(JSON.toJSONString(reportIds), hashTree, request.getReportId(), runMode, request.getConfig()); - } - + jMeterService.runSerial(JSON.toJSONString(reportIds), hashTree, request.getReportId(), runMode, request.getConfig()); } catch (Exception e) { LogUtil.error(e.getMessage()); MSException.throwException(e.getMessage()); @@ -1322,7 +1321,7 @@ public class ApiAutomationService { } APIScenarioReportResult report = createScenarioReport(request.getId(), request.getScenarioId(), request.getScenarioName(), ReportTriggerMode.MANUAL.name(), request.getExecuteType(), request.getProjectId(), - SessionUtils.getUserId(),request.getConfig()); + SessionUtils.getUserId(), request.getConfig()); apiScenarioReportMapper.insert(report); uploadBodyFiles(request.getBodyFileRequestIds(), bodyFiles); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 75a0327ea7..6c717a8f6b 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -589,7 +589,7 @@ public class ApiDefinitionService { // 调用执行方法 if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { - jMeterService.runTest(request.getId(), hashTree, runMode, request.getReportId() != null, request.getConfig()); + jMeterService.runTest(request.getId(), request.getId(), runMode, null, request.getConfig()); } else { jMeterService.runDefinition(request.getId(), hashTree, request.getReportId(), runMode); } @@ -637,6 +637,7 @@ public class ApiDefinitionService { ApiDefinitionExecResult result = extApiDefinitionExecResultMapper.selectMaxResultByResourceId(testId); return buildAPIReportResult(result); } + public APIReportResult getReportById(String testId) { ApiDefinitionExecResult result = apiDefinitionExecResultMapper.selectByPrimaryKey(testId); return buildAPIReportResult(result); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java b/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java new file mode 100644 index 0000000000..424cd55757 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/service/ApiJmeterFileService.java @@ -0,0 +1,158 @@ +package io.metersphere.api.service; + +import com.alibaba.fastjson.JSON; +import io.metersphere.api.dto.definition.request.MsTestPlan; +import io.metersphere.api.dto.scenario.request.BodyFile; +import io.metersphere.api.jmeter.JMeterService; +import io.metersphere.base.domain.ApiScenarioWithBLOBs; +import io.metersphere.base.domain.JarConfig; +import io.metersphere.base.domain.TestPlanApiScenario; +import io.metersphere.base.mapper.ApiScenarioMapper; +import io.metersphere.base.mapper.TestPlanApiScenarioMapper; +import io.metersphere.commons.constants.ApiRunMode; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.service.JarConfigService; +import io.metersphere.track.service.TestPlanApiCaseService; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.jorphan.collections.HashTree; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +@Service +public class ApiJmeterFileService { + + @Resource + private ApiAutomationService apiAutomationService; + @Resource + private TestPlanApiCaseService testPlanApiCaseService; + @Resource + private TestPlanApiScenarioMapper testPlanApiScenarioMapper; + @Resource + private ApiScenarioMapper apiScenarioMapper; + @Resource + private JMeterService jMeterService; + + public byte[] downloadJmeterFiles(String runMode, String testId, String reportId, String testPlanScenarioId) { + Map planEnvMap = new HashMap<>(); + if (StringUtils.isNotEmpty(testPlanScenarioId)) { + // 获取场景用例单独的执行环境 + TestPlanApiScenario planApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(testPlanScenarioId); + String environment = planApiScenario.getEnvironment(); + if (StringUtils.isNotBlank(environment)) { + planEnvMap = JSON.parseObject(environment, Map.class); + } + } + HashTree hashTree = null; + if (ApiRunMode.DEFINITION.name().equals(runMode) || ApiRunMode.API_PLAN.name().equals(runMode)) { + hashTree = testPlanApiCaseService.generateHashTree(testId); + } else { + ApiScenarioWithBLOBs item = apiScenarioMapper.selectByPrimaryKey(testId); + if (item == null) { + MSException.throwException("未找到执行场景。"); + } + hashTree = apiAutomationService.generateHashTree(item, reportId, planEnvMap); + } + //jMeterService.addBackendListener(reportId, hashTree); + return zipFilesToByteArray(testId, hashTree); + } + + public byte[] downloadJmeterJar() { + Map files = new HashMap<>(); + // 获取JAR + Map jarFiles = this.getJar(); + if (!com.alibaba.excel.util.CollectionUtils.isEmpty(jarFiles)) { + for (String k : jarFiles.keySet()) { + byte[] v = jarFiles.get(k); + files.put(k, v); + } + } + return listBytesToZip(files); + } + + private Map getJar() { + Map jarFiles = new LinkedHashMap<>(); + // jar 包 + JarConfigService jarConfigService = CommonBeanFactory.getBean(JarConfigService.class); + List jars = jarConfigService.list(); + jars.forEach(jarConfig -> { + try { + String path = jarConfig.getPath(); + File file = new File(path); + if (file.isDirectory() && !path.endsWith("/")) { + file = new File(path + "/"); + } + byte[] fileByte = jMeterService.fileToByte(file); + if (fileByte != null) { + jarFiles.put(file.getName(), fileByte); + } + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + } + }); + return jarFiles; + } + + private Map getMultipartFiles(HashTree hashTree) { + Map multipartFiles = new LinkedHashMap<>(); + // 获取附件 + List files = new LinkedList<>(); + jMeterService.getFiles(hashTree, files); + if (CollectionUtils.isNotEmpty(files)) { + for (BodyFile bodyFile : files) { + File file = new File(bodyFile.getName()); + if (file != null && !file.exists()) { + byte[] fileByte = jMeterService.fileToByte(file); + if (fileByte != null) { + multipartFiles.put(file.getName(), fileByte); + } + } + } + } + return multipartFiles; + } + + private byte[] zipFilesToByteArray(String testId, HashTree hashTree) { + String fileName = testId + ".jmx"; + String jmx = new MsTestPlan().getJmx(hashTree); + Map files = new HashMap<>(); + // 每个测试生成一个文件夹 + files.put(fileName, jmx.getBytes(StandardCharsets.UTF_8)); + // 获取JMX使用到的附件 + Map multipartFiles = this.getMultipartFiles(hashTree); + if (!com.alibaba.excel.util.CollectionUtils.isEmpty(multipartFiles)) { + for (String k : multipartFiles.keySet()) { + byte[] v = multipartFiles.get(k); + files.put(k, v); + } + } + return listBytesToZip(files); + } + + private byte[] listBytesToZip(Map mapReport) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(baos); + for (Map.Entry report : mapReport.entrySet()) { + ZipEntry entry = new ZipEntry(report.getKey()); + entry.setSize(report.getValue().length); + zos.putNextEntry(entry); + zos.write(report.getValue()); + } + zos.closeEntry(); + zos.close(); + return baos.toByteArray(); + } catch (Exception e) { + return null; + } + } +} diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java index c47bc4511f..d0268221ca 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java @@ -358,33 +358,6 @@ public class ApiScenarioReportService { } } } - - private void margeReport(TestResult result, StringBuilder scenarioIds, StringBuilder scenarioNames, String runMode, String projectId, String userId, List reportIds) { - // 合并生成一份报告 - if (StringUtils.isNotEmpty(result.getSetReportId())) { - // 清理其他报告保留一份合并后的报告 - this.deleteByIds(reportIds); - - ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(result.getSetReportId()); - report.setStatus(result.getError() > 0 ? "Error" : "Success"); - if (StringUtils.isNotEmpty(userId)) { - report.setUserId(userId); - } else { - report.setUserId(SessionUtils.getUserId()); - } - report.setExecuteType(ExecuteType.Saved.name()); - report.setProjectId(projectId); - report.setScenarioName(scenarioNames.toString().substring(0, scenarioNames.toString().length() - 1)); - report.setScenarioId(scenarioIds.toString()); - apiScenarioReportMapper.updateByPrimaryKey(report); - ApiScenarioReportDetail detail = new ApiScenarioReportDetail(); - detail.setContent(JSON.toJSONString(result).getBytes(StandardCharsets.UTF_8)); - detail.setReportId(report.getId()); - detail.setProjectId(report.getProjectId()); - apiScenarioReportDetailMapper.insert(detail); - } - } - public void margeReport(String reportId, List reportIds) { // 合并生成一份报告 if (CollectionUtils.isNotEmpty(reportIds)) { diff --git a/backend/src/main/java/io/metersphere/api/service/task/ParallelScenarioExecTask.java b/backend/src/main/java/io/metersphere/api/service/task/ParallelScenarioExecTask.java index bbbe1e257a..9112c110c5 100644 --- a/backend/src/main/java/io/metersphere/api/service/task/ParallelScenarioExecTask.java +++ b/backend/src/main/java/io/metersphere/api/service/task/ParallelScenarioExecTask.java @@ -3,35 +3,33 @@ */ package io.metersphere.api.service.task; +import io.metersphere.api.dto.RunModeDataDTO; import io.metersphere.api.dto.automation.RunScenarioRequest; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; import org.apache.commons.lang3.StringUtils; -import org.apache.jorphan.collections.HashTree; import java.util.concurrent.Callable; public class ParallelScenarioExecTask implements Callable { private RunScenarioRequest request; private JMeterService jMeterService; - private HashTree hashTree; - private String id; + private RunModeDataDTO runModeDataDTO; - public ParallelScenarioExecTask(JMeterService jMeterService, String id, HashTree hashTree, RunScenarioRequest request) { + public ParallelScenarioExecTask(JMeterService jMeterService, RunModeDataDTO runModeDataDTO, RunScenarioRequest request) { this.jMeterService = jMeterService; this.request = request; - this.hashTree = hashTree; - this.id = id; + this.runModeDataDTO = runModeDataDTO; } @Override public T call() { try { if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { - jMeterService.runTest(id, hashTree, request.getRunMode(), false, request.getConfig()); + jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReportId(), request.getRunMode(), request.getPlanScenarioId(), request.getConfig()); } else { - jMeterService.runSerial(id, hashTree, request.getReportId(), request.getRunMode(), request.getConfig()); + jMeterService.runSerial(runModeDataDTO.getReportId(), runModeDataDTO.getHashTree(), request.getReportId(), request.getRunMode(), request.getConfig()); } return null; } catch (Exception ex) { diff --git a/backend/src/main/java/io/metersphere/api/service/task/SerialScenarioExecTask.java b/backend/src/main/java/io/metersphere/api/service/task/SerialScenarioExecTask.java index 2dec30ad69..10ed0a5e82 100644 --- a/backend/src/main/java/io/metersphere/api/service/task/SerialScenarioExecTask.java +++ b/backend/src/main/java/io/metersphere/api/service/task/SerialScenarioExecTask.java @@ -3,6 +3,7 @@ */ package io.metersphere.api.service.task; +import io.metersphere.api.dto.RunModeDataDTO; import io.metersphere.api.dto.automation.RunScenarioRequest; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.base.domain.ApiScenarioReport; @@ -11,7 +12,6 @@ import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; import org.apache.commons.lang3.StringUtils; -import org.apache.jorphan.collections.HashTree; import java.util.concurrent.Callable; @@ -19,32 +19,30 @@ public class SerialScenarioExecTask implements Callable { private RunScenarioRequest request; private JMeterService jMeterService; private ApiScenarioReportMapper apiScenarioReportMapper; - private HashTree hashTree; + private RunModeDataDTO runModeDataDTO; ApiScenarioReport report = null; - private String id; - public SerialScenarioExecTask(JMeterService jMeterService, ApiScenarioReportMapper apiScenarioReportMapper, String id, HashTree hashTree, RunScenarioRequest request) { + public SerialScenarioExecTask(JMeterService jMeterService, ApiScenarioReportMapper apiScenarioReportMapper, RunModeDataDTO runModeDataDTO, RunScenarioRequest request) { this.jMeterService = jMeterService; this.apiScenarioReportMapper = apiScenarioReportMapper; this.request = request; - this.hashTree = hashTree; - this.id = id; + this.runModeDataDTO = runModeDataDTO; } @Override public T call() { try { if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { - jMeterService.runTest(id, hashTree, request.getRunMode(), false, request.getConfig()); + jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReportId(), request.getRunMode(), request.getPlanScenarioId(), request.getConfig()); } else { - jMeterService.runSerial(id, hashTree, request.getReportId(), request.getRunMode(), request.getConfig()); + jMeterService.runSerial(runModeDataDTO.getReportId(), runModeDataDTO.getHashTree(), request.getReportId(), request.getRunMode(), request.getConfig()); } // 轮询查看报告状态,最多200次,防止死循环 int index = 1; while (index < 200) { Thread.sleep(3000); index++; - report = apiScenarioReportMapper.selectByPrimaryKey(id); + report = apiScenarioReportMapper.selectByPrimaryKey(runModeDataDTO.getReportId()); if (report != null && !report.getStatus().equals(APITestStatus.Running.name())) { break; } diff --git a/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java b/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java index 888483190c..e4565ff11d 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java @@ -34,6 +34,8 @@ public class ShiroUtils { filterChainDefinitionMap.put("/sso/signin", "anon"); filterChainDefinitionMap.put("/sso/callback", "anon"); filterChainDefinitionMap.put("/license/valid", "anon"); + filterChainDefinitionMap.put("/api/jmeter/download", "anon"); + filterChainDefinitionMap.put("/api/jmeter/download/jar", "anon"); // for swagger filterChainDefinitionMap.put("/swagger-ui.html", "anon"); diff --git a/backend/src/main/java/io/metersphere/track/controller/TestPlanApiCaseController.java b/backend/src/main/java/io/metersphere/track/controller/TestPlanApiCaseController.java index 1d70db3cc4..7d5264b6e6 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestPlanApiCaseController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestPlanApiCaseController.java @@ -4,18 +4,16 @@ import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseRequest; +import io.metersphere.api.dto.definition.BatchRunDefinitionRequest; import io.metersphere.api.dto.definition.TestPlanApiCaseDTO; import io.metersphere.commons.constants.OperLogConstants; import io.metersphere.commons.constants.PermissionConstants; -import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; import io.metersphere.log.annotation.MsAuditLog; import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest; import io.metersphere.track.service.TestPlanApiCaseService; -import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresPermissions; -import org.apache.shiro.authz.annotation.RequiresRoles; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -66,4 +64,8 @@ public class TestPlanApiCaseController { testPlanApiCaseService.batchUpdateEnv(request); } + @PostMapping(value = "/run") + public String run(@RequestPart("request") BatchRunDefinitionRequest request) { + return testPlanApiCaseService.run(request); + } } diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java index 27f0afff5e..af05084745 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java @@ -1,24 +1,46 @@ package io.metersphere.track.service; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; +import io.metersphere.api.dto.RunModeDataDTO; import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseRequest; +import io.metersphere.api.dto.definition.BatchRunDefinitionRequest; import io.metersphere.api.dto.definition.TestPlanApiCaseDTO; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.MsTestPlan; +import io.metersphere.api.dto.definition.request.MsThreadGroup; +import io.metersphere.api.dto.definition.request.ParameterConfig; +import io.metersphere.api.dto.definition.request.sampler.MsDubboSampler; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; +import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler; +import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler; +import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.service.ApiDefinitionExecResultService; import io.metersphere.api.service.ApiTestCaseService; import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; import io.metersphere.base.mapper.ApiTestCaseMapper; import io.metersphere.base.mapper.TestPlanApiCaseMapper; import io.metersphere.base.mapper.TestPlanMapper; import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper; -import io.metersphere.commons.utils.PageUtils; -import io.metersphere.commons.utils.Pager; -import io.metersphere.commons.utils.ServiceUtils; -import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.commons.constants.ApiRunMode; +import io.metersphere.commons.constants.RunModeConstants; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.*; +import io.metersphere.dto.BaseSystemConfigDTO; import io.metersphere.log.vo.OperatingLogDetails; +import io.metersphere.service.SystemParameterService; import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest; +import io.metersphere.track.service.task.ParallelApiExecTask; +import io.metersphere.track.service.task.SerialApiExecTask; +import org.apache.commons.lang3.StringUtils; +import org.apache.jorphan.collections.HashTree; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,6 +48,9 @@ import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.stream.Collectors; @Service @@ -45,6 +70,12 @@ public class TestPlanApiCaseService { private TestPlanMapper testPlanMapper; @Resource ApiTestCaseMapper apiTestCaseMapper; + @Resource + private SystemParameterService systemParameterService; + @Resource + private JMeterService jMeterService; + @Resource + private ApiDefinitionExecResultMapper mapper; public TestPlanApiCase getInfo(String caseId, String testPlanId) { TestPlanApiCaseExample example = new TestPlanApiCaseExample(); @@ -219,4 +250,169 @@ public class TestPlanApiCaseService { } return null; } + + + private MsTestElement parse(String api, String planId) { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + try { + JSONObject element = JSON.parseObject(api); + LinkedList list = new LinkedList<>(); + if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) { + LinkedList elements = mapper.readValue(element.getString("hashTree"), + new TypeReference>() { + }); + list.addAll(elements); + } + TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(planId); + Map envMap = null; + if (apiCase != null) { + envMap = JSON.parseObject(apiCase.getEnvironmentId(), Map.class); + } + if (element.getString("type").equals("HTTPSamplerProxy")) { + MsHTTPSamplerProxy httpSamplerProxy = JSON.parseObject(api, MsHTTPSamplerProxy.class); + httpSamplerProxy.setHashTree(list); + if (envMap != null && envMap.containsKey(httpSamplerProxy.getProjectId())) { + httpSamplerProxy.setUseEnvironment(envMap.get(httpSamplerProxy.getProjectId())); + } + return httpSamplerProxy; + } + if (element.getString("type").equals("TCPSampler")) { + MsTCPSampler msTCPSampler = JSON.parseObject(api, MsTCPSampler.class); + if (envMap != null && envMap.containsKey(msTCPSampler.getProjectId())) { + msTCPSampler.setUseEnvironment(envMap.get(msTCPSampler.getProjectId())); + } + msTCPSampler.setHashTree(list); + return msTCPSampler; + } + if (element.getString("type").equals("DubboSampler")) { + MsDubboSampler dubboSampler = JSON.parseObject(api, MsDubboSampler.class); + if (envMap != null && envMap.containsKey(dubboSampler.getProjectId())) { + dubboSampler.setUseEnvironment(envMap.get(dubboSampler.getProjectId())); + } + dubboSampler.setHashTree(list); + return dubboSampler; + } + if (element.getString("type").equals("JDBCSampler")) { + MsJDBCSampler jDBCSampler = JSON.parseObject(api, MsJDBCSampler.class); + if (envMap != null && envMap.containsKey(jDBCSampler.getProjectId())) { + jDBCSampler.setUseEnvironment(envMap.get(jDBCSampler.getProjectId())); + } + jDBCSampler.setHashTree(list); + return jDBCSampler; + } + } catch (Exception e) { + e.printStackTrace(); + LogUtil.error(e.getMessage()); + } + return null; + } + + public HashTree generateHashTree(String testId) { + TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(testId); + if (apiCase != null) { + ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(apiCase.getApiCaseId()); + HashTree jmeterHashTree = new HashTree(); + MsTestPlan testPlan = new MsTestPlan(); + testPlan.setHashTree(new LinkedList<>()); + if (caseWithBLOBs != null) { + try { + MsThreadGroup group = new MsThreadGroup(); + group.setLabel(caseWithBLOBs.getName()); + group.setName(testId); + MsTestElement testElement = parse(caseWithBLOBs.getRequest(), testId); + group.setHashTree(new LinkedList<>()); + group.getHashTree().add(testElement); + testPlan.getHashTree().add(group); + } catch (Exception ex) { + MSException.throwException(ex.getMessage()); + } + } + testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig()); + return jmeterHashTree; + } + return null; + } + + public String modeRun(BatchRunDefinitionRequest request) { + List ids = request.getPlanIds(); + TestPlanApiCaseExample example = new TestPlanApiCaseExample(); + example.createCriteria().andIdIn(ids); + List planApiCases = testPlanApiCaseMapper.selectByExample(example); + + // 开始选择执行模式 + ExecutorService executorService = Executors.newFixedThreadPool(planApiCases.size()); + if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { + // 开始串行执行 + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + for (TestPlanApiCase key : planApiCases) { + try { + RunModeDataDTO modeDataDTO = null; + if (StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { + modeDataDTO = new RunModeDataDTO(key.getId(), UUID.randomUUID().toString()); + } else { + // 生成报告和HashTree + HashTree hashTree = generateHashTree(key.getId()); + modeDataDTO = new RunModeDataDTO(hashTree, UUID.randomUUID().toString()); + } + Future future = executorService.submit(new SerialApiExecTask(jMeterService, mapper, modeDataDTO, request.getConfig(), ApiRunMode.API_PLAN.name())); + ApiDefinitionExecResult report = future.get(); + // 如果开启失败结束执行,则判断返回结果状态 + if (request.getConfig().isOnSampleError()) { + if (report == null || !report.getStatus().equals("Success")) { + break; + } + } + } catch (Exception e) { + LogUtil.error("执行终止:" + e.getMessage()); + break; + } + } + } + }); + thread.start(); + } else { + // 开始并发执行 + for (TestPlanApiCase key : planApiCases) { + RunModeDataDTO modeDataDTO = null; + if (StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { + modeDataDTO = new RunModeDataDTO(key.getId(), UUID.randomUUID().toString()); + } else { + // 生成报告和HashTree + HashTree hashTree = generateHashTree(key.getId()); + modeDataDTO = new RunModeDataDTO(hashTree, UUID.randomUUID().toString()); + } + executorService.submit(new ParallelApiExecTask(jMeterService, mapper, modeDataDTO, request.getConfig(), ApiRunMode.API_PLAN.name())); + } + } + return request.getId(); + } + + /** + * 测试执行 + * + * @param request + * @return + */ + public String run(BatchRunDefinitionRequest request) { + if (request.getConfig() != null) { + if (request.getConfig().getMode().equals(RunModeConstants.PARALLEL.toString())) { + // 校验并发数量 + int count = 50; + BaseSystemConfigDTO dto = systemParameterService.getBaseInfo(); + if (StringUtils.isNotEmpty(dto.getConcurrency())) { + count = Integer.parseInt(dto.getConcurrency()); + } + if (request.getPlanIds().size() > count) { + MSException.throwException("并发数量过大,请重新选择!"); + } + return this.modeRun(request); + } else { + return this.modeRun(request); + } + } + return request.getId(); + } } diff --git a/backend/src/main/java/io/metersphere/track/service/task/ParallelApiExecTask.java b/backend/src/main/java/io/metersphere/track/service/task/ParallelApiExecTask.java new file mode 100644 index 0000000000..b13bd77c9e --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/service/task/ParallelApiExecTask.java @@ -0,0 +1,46 @@ +/** + * + */ +package io.metersphere.track.service.task; + +import io.metersphere.api.dto.RunModeDataDTO; +import io.metersphere.api.dto.automation.RunModeConfig; +import io.metersphere.api.jmeter.JMeterService; +import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.LogUtil; +import org.apache.commons.lang3.StringUtils; + +import java.util.concurrent.Callable; + +public class ParallelApiExecTask implements Callable { + private RunModeConfig config; + private JMeterService jMeterService; + private RunModeDataDTO runModeDataDTO; + private String runMode; + private ApiDefinitionExecResultMapper mapper; + + public ParallelApiExecTask(JMeterService jMeterService, ApiDefinitionExecResultMapper mapper, RunModeDataDTO runModeDataDTO, RunModeConfig config, String runMode) { + this.jMeterService = jMeterService; + this.config = config; + this.runModeDataDTO = runModeDataDTO; + this.runMode = runMode; + this.mapper = mapper; + } + + @Override + public T call() { + try { + if (config != null && StringUtils.isNotBlank(config.getResourcePoolId())) { + jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReportId(), runMode, null, config); + } else { + jMeterService.runDefinition(runModeDataDTO.getReportId(), runModeDataDTO.getHashTree(), null, runMode); + } + return null; + } catch (Exception ex) { + LogUtil.error(ex.getMessage()); + MSException.throwException(ex.getMessage()); + return null; + } + } +} diff --git a/backend/src/main/java/io/metersphere/track/service/task/SerialApiExecTask.java b/backend/src/main/java/io/metersphere/track/service/task/SerialApiExecTask.java new file mode 100644 index 0000000000..92814613bc --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/service/task/SerialApiExecTask.java @@ -0,0 +1,58 @@ +/** + * + */ +package io.metersphere.track.service.task; + +import io.metersphere.api.dto.RunModeDataDTO; +import io.metersphere.api.dto.automation.RunModeConfig; +import io.metersphere.api.jmeter.JMeterService; +import io.metersphere.base.domain.ApiDefinitionExecResult; +import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.LogUtil; +import org.apache.commons.lang3.StringUtils; + +import java.util.concurrent.Callable; + +public class SerialApiExecTask implements Callable { + private RunModeConfig config; + private JMeterService jMeterService; + private RunModeDataDTO runModeDataDTO; + private String runMode; + private ApiDefinitionExecResultMapper mapper; + + public SerialApiExecTask(JMeterService jMeterService, ApiDefinitionExecResultMapper mapper, RunModeDataDTO runModeDataDTO, RunModeConfig config, String runMode) { + this.jMeterService = jMeterService; + this.config = config; + this.runModeDataDTO = runModeDataDTO; + this.runMode = runMode; + this.mapper = mapper; + } + + @Override + public T call() { + try { + if (config != null && StringUtils.isNotBlank(config.getResourcePoolId())) { + jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReportId(), runMode, null, config); + } else { + jMeterService.runDefinition(runModeDataDTO.getReportId(), runModeDataDTO.getHashTree(), null, runMode); + } + // 轮询查看报告状态,最多200次,防止死循环 + ApiDefinitionExecResult report = null; + int index = 1; + while (index < 200) { + Thread.sleep(3000); + index++; + report = mapper.selectByPrimaryKey(runModeDataDTO.getReportId()); + if (report != null) { + break; + } + } + return (T) report; + } catch (Exception ex) { + LogUtil.error(ex.getMessage()); + MSException.throwException(ex.getMessage()); + return null; + } + } +} \ No newline at end of file