fix (接口自动化): 接口自动化重构并发执行

This commit is contained in:
fit2-zhao 2021-08-27 21:07:14 +08:00 committed by fit2-zhao
parent f9eae5ce44
commit 1ad1f9a2b9
17 changed files with 273 additions and 131 deletions

View File

@ -1,15 +1,15 @@
package io.metersphere.api.controller; package io.metersphere.api.controller;
import io.metersphere.api.dto.scenario.request.BodyFile;
import io.metersphere.api.service.ApiJmeterFileService; import io.metersphere.api.service.ApiJmeterFileService;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
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 javax.annotation.Resource;
import java.util.List;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
@ -19,15 +19,23 @@ public class ApiJmeterFileController {
@Resource @Resource
private ApiJmeterFileService apiJmeterFileService; private ApiJmeterFileService apiJmeterFileService;
@GetMapping("download") @PostMapping("download/files")
public ResponseEntity<byte[]> downloadJmeterFiles(@RequestParam("testId") String testId, @RequestParam("reportId") String reportId, @RequestParam("runMode") String runMode, @RequestParam("testPlanScenarioId") String testPlanScenarioId) { public ResponseEntity<byte[]> downloadJmeterFiles(@RequestBody List<BodyFile> bodyFileList) {
byte[] bytes = apiJmeterFileService.downloadJmeterFiles(runMode,testId, reportId, testPlanScenarioId); byte[] bytes = new byte[10];
if (CollectionUtils.isNotEmpty(bodyFileList)) {
bytes = apiJmeterFileService.downloadJmeterFiles(bodyFileList);
}
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream")) .contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + testId + ".zip\"") .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + UUID.randomUUID().toString() + ".zip\"")
.body(bytes); .body(bytes);
} }
@GetMapping("download")
public byte[] downloadJmx(@RequestParam("testId") String testId, @RequestParam("reportId") String reportId, @RequestParam("runMode") String runMode, @RequestParam("testPlanScenarioId") String testPlanScenarioId) {
return apiJmeterFileService.downloadJmx(runMode, testId, reportId, testPlanScenarioId);
}
@GetMapping("download/jar") @GetMapping("download/jar")
public ResponseEntity<byte[]> downloadJmeterFiles() { public ResponseEntity<byte[]> downloadJmeterFiles() {
byte[] bytes = apiJmeterFileService.downloadJmeterJar(); byte[] bytes = apiJmeterFileService.downloadJmeterJar();

View File

@ -1,8 +1,11 @@
package io.metersphere.api.dto.automation; package io.metersphere.api.dto.automation;
import io.metersphere.api.dto.JvmInfoDTO;
import io.metersphere.dto.BaseSystemConfigDTO;
import lombok.Data; import lombok.Data;
import java.util.Map; import java.util.Map;
import java.util.List;
@Data @Data
public class RunModeConfig { public class RunModeConfig {
@ -12,7 +15,8 @@ public class RunModeConfig {
private String reportId; private String reportId;
private boolean onSampleError; private boolean onSampleError;
private String resourcePoolId; private String resourcePoolId;
private BaseSystemConfigDTO baseInfo;
private List<JvmInfoDTO> testResources;
/** /**
* 运行环境 * 运行环境
*/ */

View File

@ -7,8 +7,6 @@ import io.metersphere.api.service.MsResultService;
import io.metersphere.api.service.TestResultService; import io.metersphere.api.service.TestResultService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.SampleResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Comparator; import java.util.Comparator;
@ -20,8 +18,6 @@ import java.util.Map;
* 获取结果和数据库操作分离 * 获取结果和数据库操作分离
* 减少占用的数据库连接 * 减少占用的数据库连接
*/ */
@Service
@Transactional(rollbackFor = Exception.class)
public class APIBackendListenerHandler { public class APIBackendListenerHandler {
@Resource @Resource

View File

@ -1,6 +1,7 @@
package io.metersphere.api.jmeter; package io.metersphere.api.jmeter;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import io.metersphere.api.dto.JvmInfoDTO;
import io.metersphere.api.dto.RunRequest; import io.metersphere.api.dto.RunRequest;
import io.metersphere.api.dto.automation.ExecuteType; import io.metersphere.api.dto.automation.ExecuteType;
import io.metersphere.api.dto.automation.RunModeConfig; import io.metersphere.api.dto.automation.RunModeConfig;
@ -10,9 +11,9 @@ import io.metersphere.base.domain.TestResourcePool;
import io.metersphere.base.mapper.TestResourcePoolMapper; import io.metersphere.base.mapper.TestResourcePoolMapper;
import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ResourcePoolTypeEnum; import io.metersphere.commons.constants.ResourcePoolTypeEnum;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.*;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.config.JmeterProperties; import io.metersphere.config.JmeterProperties;
import io.metersphere.dto.BaseSystemConfigDTO; import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.NodeDTO; import io.metersphere.dto.NodeDTO;
@ -20,7 +21,15 @@ import io.metersphere.i18n.Translator;
import io.metersphere.performance.engine.Engine; import io.metersphere.performance.engine.Engine;
import io.metersphere.performance.engine.EngineFactory; import io.metersphere.performance.engine.EngineFactory;
import io.metersphere.service.SystemParameterService; import io.metersphere.service.SystemParameterService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.save.SaveService; import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.TestElement;
@ -35,9 +44,9 @@ import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.File; import java.io.*;
import java.io.InputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.List;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -128,17 +137,21 @@ public class JMeterService {
init(); init();
FixedTask.tasks.put(testId, System.currentTimeMillis()); FixedTask.tasks.put(testId, System.currentTimeMillis());
addBackendListener(testId, debugReportId, runMode, testPlan); addBackendListener(testId, debugReportId, runMode, testPlan);
if (ExecuteType.Debug.name().equals(debugReportId) || ApiRunMode.SCENARIO.name().equals(runMode)) { if (ExecuteType.Debug.name().equals(debugReportId) || (ApiRunMode.SCENARIO.name().equals(runMode) && !TriggerMode.BATCH.name().equals(debugReportId))) {
addResultCollector(testId, testPlan); addResultCollector(testId, testPlan);
} }
LocalRunner runner = new LocalRunner(testPlan); LocalRunner runner = new LocalRunner(testPlan);
runner.run(testId); runner.run(testId);
} }
public void runTest(String testId, String reportId, String runMode, String testPlanScenarioId, RunModeConfig config) { public void runTest(String testId, String reportId, String runMode,
String testPlanScenarioId, RunModeConfig config) {
// 获取可以执行的资源池 // 获取可以执行的资源池
String resourcePoolId = config.getResourcePoolId(); String resourcePoolId = config.getResourcePoolId();
BaseSystemConfigDTO baseInfo = CommonBeanFactory.getBean(SystemParameterService.class).getBaseInfo(); BaseSystemConfigDTO baseInfo = config.getBaseInfo();
if (baseInfo == null) {
baseInfo = CommonBeanFactory.getBean(SystemParameterService.class).getBaseInfo();
}
RunRequest runRequest = new RunRequest(); RunRequest runRequest = new RunRequest();
runRequest.setTestId(testId); runRequest.setTestId(testId);
runRequest.setReportId(reportId); runRequest.setReportId(reportId);
@ -166,15 +179,24 @@ public class JMeterService {
MSException.throwException(e.getMessage()); MSException.throwException(e.getMessage());
} }
} else { } else {
TestResource testResource = resourcePoolCalculation.getPool(resourcePoolId); TestResource testResource = null;
List<JvmInfoDTO> testResources = config.getTestResources();
if (CollectionUtils.isEmpty(testResources)) {
testResource = resourcePoolCalculation.getPool(resourcePoolId);
} else {
int index = (int) (Math.random() * testResources.size());
JvmInfoDTO jvmInfoDTO = testResources.get(index);
testResource = testResources.get(index).getTestResource();
}
String configuration = testResource.getConfiguration(); String configuration = testResource.getConfiguration();
NodeDTO node = JSON.parseObject(configuration, NodeDTO.class); NodeDTO node = JSON.parseObject(configuration, NodeDTO.class);
String nodeIp = node.getIp(); String nodeIp = node.getIp();
Integer port = node.getPort(); Integer port = node.getPort();
try { try {
String uri = String.format(BASE_URL + "/jmeter/api/start", nodeIp, port); String uri = String.format(BASE_URL + "/jmeter/api/start", nodeIp, port);
ResponseEntity<String> result = restTemplate.postForEntity(uri, runRequest, String.class); ResponseEntity<String> resultEntity = restTemplate.postForEntity(uri, runRequest, String.class);
if (result == null || !StringUtils.equals("SUCCESS", result.getBody())) { String result = resultEntity.getBody(); // this.send(uri, runRequest);
if (StringUtils.isEmpty(result) || !StringUtils.equals("SUCCESS", result)) {
// 清理零时报告 // 清理零时报告
ApiScenarioReportService apiScenarioReportService = CommonBeanFactory.getBean(ApiScenarioReportService.class); ApiScenarioReportService apiScenarioReportService = CommonBeanFactory.getBean(ApiScenarioReportService.class);
apiScenarioReportService.delete(reportId); apiScenarioReportService.delete(reportId);
@ -182,7 +204,31 @@ public class JMeterService {
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
MSException.throwException(e.getMessage()); MSException.throwException(runRequest.getReportId() + "" + e.getMessage());
}
}
}
public String send(String webhook, RunRequest runRequest) {
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(webhook);
// 创建请求内容
StringEntity entity = new StringEntity(JSON.toJSONString(runRequest), ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
String result = EntityUtils.toString(response.getEntity());
return result;
} catch (Exception e) {
return e.getMessage();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
} }
} }
} }

View File

@ -10,12 +10,10 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener; import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
@Service @Service
@Transactional(rollbackFor = Exception.class)
public class MsKafkaListener { public class MsKafkaListener {
public static final String TOPICS = "ms-api-exec-topic"; public static final String TOPICS = "ms-api-exec-topic";
public static final String CONSUME_ID = "ms-api-exec-consume"; public static final String CONSUME_ID = "ms-api-exec-consume";
@ -23,7 +21,11 @@ public class MsKafkaListener {
@KafkaListener(id = CONSUME_ID, topics = TOPICS, groupId = "${spring.kafka.consumer.group-id}") @KafkaListener(id = CONSUME_ID, topics = TOPICS, groupId = "${spring.kafka.consumer.group-id}")
public void consume(ConsumerRecord<?, String> record) { public void consume(ConsumerRecord<?, String> record) {
LogUtil.info("接收到执行结果开始存储"); LogUtil.info("接收到执行结果开始存储");
this.save(record.value()); try {
this.save(record.value());
} catch (Exception e) {
LogUtil.error(e.getMessage());
}
LogUtil.info("执行内容存储结束"); LogUtil.info("执行内容存储结束");
} }

View File

@ -16,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -41,7 +42,7 @@ public class ResourcePoolCalculation {
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(resourcePoolId); example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(resourcePoolId);
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example); List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
// 按照NODE节点的可用内存空间大小排序 // 按照NODE节点的可用内存空间大小排序
JvmInfoDTO jvmInfoDTO = null; List<JvmInfoDTO> availableNodes = new ArrayList<>();
if (CollectionUtils.isNotEmpty(pools)) { if (CollectionUtils.isNotEmpty(pools)) {
List<String> poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList()); List<String> poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList());
TestResourceExample resourceExample = new TestResourceExample(); TestResourceExample resourceExample = new TestResourceExample();
@ -57,19 +58,49 @@ public class ResourcePoolCalculation {
if (nodeJvm == null) { if (nodeJvm == null) {
continue; continue;
} }
// 优先取资源充足的节点如果当前节点资源超过1G就不需要在排序了 nodeJvm.setTestResource(testResource);
if (nodeJvm.getVmFree() > 1024) { availableNodes.add(nodeJvm);
return testResource;
}
if (jvmInfoDTO == null || jvmInfoDTO.getVmFree() < nodeJvm.getVmFree()) {
jvmInfoDTO = nodeJvm;
jvmInfoDTO.setTestResource(testResource);
}
} }
} }
if (jvmInfoDTO == null || jvmInfoDTO.getTestResource() == null) { if (CollectionUtils.isEmpty(availableNodes)) {
MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】"); MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】");
} }
int index = (int) (Math.random() * availableNodes.size());
JvmInfoDTO jvmInfoDTO = availableNodes.get(index);
return jvmInfoDTO.getTestResource(); return jvmInfoDTO.getTestResource();
} }
public List<JvmInfoDTO> getPools(String resourcePoolId) {
// 获取可以执行的资源池
TestResourcePoolExample example = new TestResourcePoolExample();
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(resourcePoolId);
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
// 按照NODE节点的可用内存空间大小排序
List<JvmInfoDTO> availableNodes = new ArrayList<>();
if (CollectionUtils.isNotEmpty(pools)) {
List<String> poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList());
TestResourceExample resourceExample = new TestResourceExample();
resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds);
List<TestResource> testResources = testResourceMapper.selectByExampleWithBLOBs(resourceExample);
for (TestResource testResource : testResources) {
String configuration = testResource.getConfiguration();
NodeDTO node = JSON.parseObject(configuration, NodeDTO.class);
String nodeIp = node.getIp();
Integer port = node.getPort();
String uri = String.format(BASE_URL + "/jmeter/getJvmInfo", nodeIp, port);
JvmInfoDTO nodeJvm = this.getNodeJvmInfo(uri);
if (nodeJvm == null) {
continue;
}
nodeJvm.setTestResource(testResource);
availableNodes.add(nodeJvm);
}
}
if (CollectionUtils.isEmpty(availableNodes)) {
MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】");
}
return availableNodes;
}
} }

View File

@ -23,6 +23,7 @@ import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.MessageCache; import io.metersphere.api.jmeter.MessageCache;
import io.metersphere.api.jmeter.ReportCounter; import io.metersphere.api.jmeter.ReportCounter;
import io.metersphere.api.jmeter.ResourcePoolCalculation;
import io.metersphere.api.parse.ApiImportParser; import io.metersphere.api.parse.ApiImportParser;
import io.metersphere.api.service.task.ParallelScenarioExecTask; import io.metersphere.api.service.task.ParallelScenarioExecTask;
import io.metersphere.api.service.task.SerialScenarioExecTask; import io.metersphere.api.service.task.SerialScenarioExecTask;
@ -129,6 +130,8 @@ public class ApiAutomationService {
private TcpApiParamService tcpApiParamService; private TcpApiParamService tcpApiParamService;
@Resource @Resource
private ApiScenarioReferenceIdService apiScenarioReferenceIdService; private ApiScenarioReferenceIdService apiScenarioReferenceIdService;
@Resource
private ResourcePoolCalculation resourcePoolCalculation;
public ApiScenarioWithBLOBs getDto(String id) { public ApiScenarioWithBLOBs getDto(String id) {
return apiScenarioMapper.selectByPrimaryKey(id); return apiScenarioMapper.selectByPrimaryKey(id);
@ -813,7 +816,7 @@ public class ApiAutomationService {
return null; return null;
} }
public APIScenarioReportResult createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID, RunModeConfig config,String desc) { public APIScenarioReportResult createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID, RunModeConfig config, String desc) {
APIScenarioReportResult report = new APIScenarioReportResult(); APIScenarioReportResult report = new APIScenarioReportResult();
if (triggerMode.equals(ApiRunMode.SCENARIO.name()) || triggerMode.equals(ApiRunMode.DEFINITION.name())) { if (triggerMode.equals(ApiRunMode.SCENARIO.name()) || triggerMode.equals(ApiRunMode.DEFINITION.name())) {
triggerMode = ReportTriggerMode.MANUAL.name(); triggerMode = ReportTriggerMode.MANUAL.name();
@ -989,6 +992,11 @@ public class ApiAutomationService {
if (apiScenarios != null && apiScenarios.size() == 1 && (apiScenarios.get(0).getStepTotal() == null || apiScenarios.get(0).getStepTotal() == 0)) { if (apiScenarios != null && apiScenarios.size() == 1 && (apiScenarios.get(0).getStepTotal() == null || apiScenarios.get(0).getStepTotal() == 0)) {
MSException.throwException((apiScenarios.get(0).getName() + "" + Translator.get("automation_exec_info"))); MSException.throwException((apiScenarios.get(0).getName() + "" + Translator.get("automation_exec_info")));
} }
// 资源池
if (request.getConfig() != null && StringUtils.isNotEmpty(request.getConfig().getResourcePoolId())) {
List<JvmInfoDTO> testResources = resourcePoolCalculation.getPools(request.getConfig().getResourcePoolId());
request.getConfig().setTestResources(testResources);
}
// 环境检查 // 环境检查
this.checkEnv(request, apiScenarios); this.checkEnv(request, apiScenarios);
// 集合报告设置 // 集合报告设置
@ -1026,21 +1034,21 @@ public class ApiAutomationService {
planEnvMap = JSON.parseObject(environment, Map.class); planEnvMap = JSON.parseObject(environment, Map.class);
} }
} }
String projectId = testPlanScenarioCaseService.getProjectIdById(testPlanScenarioId); String projectId = testPlanScenarioCaseService.getProjectIdById(testPlanScenarioId);
if(StringUtils.isEmpty(projectId)){ if (StringUtils.isEmpty(projectId)) {
projectId = item.getProjectId(); projectId = item.getProjectId();
} }
if (request.isTestPlanScheduleJob()) { if (request.isTestPlanScheduleJob()) {
String savedScenarioId = testPlanScenarioId + ":" + request.getTestPlanReportId(); String savedScenarioId = testPlanScenarioId + ":" + request.getTestPlanReportId();
report = createScenarioReport(reportId, savedScenarioId, item.getName(), request.getTriggerMode(), report = createScenarioReport(reportId, savedScenarioId, item.getName(), request.getTriggerMode(),
request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(),item.getId()); request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(), item.getId());
} else { } else {
report = createScenarioReport(reportId, testPlanScenarioId, item.getName(), request.getTriggerMode(), report = createScenarioReport(reportId, testPlanScenarioId, item.getName(), request.getTriggerMode(),
request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(),item.getId()); request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(), item.getId());
} }
} else { } else {
report = createScenarioReport(reportId, ExecuteType.Marge.name().equals(request.getExecuteType()) ? serialReportId : item.getId(), item.getName(), request.getTriggerMode(), report = createScenarioReport(reportId, ExecuteType.Marge.name().equals(request.getExecuteType()) ? serialReportId : item.getId(), item.getName(), request.getTriggerMode(),
request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig(),item.getId()); request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig(), item.getId());
} }
try { try {
if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) {
@ -1064,7 +1072,7 @@ public class ApiAutomationService {
APIScenarioReportResult report = createScenarioReport(request.getConfig().getReportId(), APIScenarioReportResult report = createScenarioReport(request.getConfig().getReportId(),
JSON.toJSONString(CollectionUtils.isNotEmpty(scenarioIds) && scenarioIds.size() > 50 ? scenarioIds.subList(0, 50) : scenarioIds), JSON.toJSONString(CollectionUtils.isNotEmpty(scenarioIds) && scenarioIds.size() > 50 ? scenarioIds.subList(0, 50) : scenarioIds),
scenarioNames.length() >= 3000 ? scenarioNames.substring(0, 2000) : scenarioNames.deleteCharAt(scenarioNames.toString().length() - 1).toString(), scenarioNames.length() >= 3000 ? scenarioNames.substring(0, 2000) : scenarioNames.deleteCharAt(scenarioNames.toString().length() - 1).toString(),
ReportTriggerMode.MANUAL.name(), ExecuteType.Saved.name(), request.getProjectId(), request.getReportUserID(), request.getConfig(),JSON.toJSONString(scenarioIds)); ReportTriggerMode.MANUAL.name(), ExecuteType.Saved.name(), request.getProjectId(), request.getReportUserID(), request.getConfig(), JSON.toJSONString(scenarioIds));
report.setName(request.getConfig().getReportName()); report.setName(request.getConfig().getReportName());
report.setId(serialReportId); report.setId(serialReportId);
@ -1087,7 +1095,7 @@ public class ApiAutomationService {
private void run(Map<String, RunModeDataDTO> executeQueue, RunScenarioRequest request, String serialReportId) { private void run(Map<String, RunModeDataDTO> executeQueue, RunScenarioRequest request, String serialReportId) {
// 开始选择执行模式 // 开始选择执行模式
if (executeQueue != null && executeQueue.size() > 0) { if (executeQueue != null && executeQueue.size() > 0) {
ExecutorService executorService = Executors.newFixedThreadPool(executeQueue.size()); ExecutorService executorService = Executors.newFixedThreadPool(6);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiScenarioReportMapper batchMapper = sqlSession.getMapper(ApiScenarioReportMapper.class); ApiScenarioReportMapper batchMapper = sqlSession.getMapper(ApiScenarioReportMapper.class);
if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) {
@ -1175,9 +1183,11 @@ public class ApiAutomationService {
//存储报告 //存储报告
APIScenarioReportResult report = executeQueue.get(reportId).getReport(); APIScenarioReportResult report = executeQueue.get(reportId).getReport();
batchMapper.insert(report); batchMapper.insert(report);
executorService.submit(new ParallelScenarioExecTask(jMeterService, executeQueue.get(reportId), request));
} }
sqlSession.flushStatements(); sqlSession.flushStatements();
for (String reportId : executeQueue.keySet()) {
executorService.submit(new ParallelScenarioExecTask(jMeterService, executeQueue.get(reportId), request));
}
} }
} }
} }
@ -1256,22 +1266,22 @@ public class ApiAutomationService {
} }
} }
String projectId = testPlanScenarioCaseService.getProjectIdById(testPlanScenarioId); String projectId = testPlanScenarioCaseService.getProjectIdById(testPlanScenarioId);
if(StringUtils.isEmpty(projectId)){ if (StringUtils.isEmpty(projectId)) {
projectId = item.getProjectId(); projectId = item.getProjectId();
} }
if (request.isTestPlanScheduleJob()) { if (request.isTestPlanScheduleJob()) {
String savedScenarioId = testPlanScenarioId + ":" + request.getTestPlanReportId(); String savedScenarioId = testPlanScenarioId + ":" + request.getTestPlanReportId();
report = createScenarioReport(group.getName(), savedScenarioId, item.getName(), request.getTriggerMode(), report = createScenarioReport(group.getName(), savedScenarioId, item.getName(), request.getTriggerMode(),
request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(),item.getId()); request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(), item.getId());
} else { } else {
report = createScenarioReport(group.getName(), testPlanScenarioId, item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), report = createScenarioReport(group.getName(), testPlanScenarioId, item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(),item.getId()); request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig(), item.getId());
} }
} else { } else {
report = createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), report = createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig(),item.getId()); request.getExecuteType(), item.getProjectId(), request.getReportUserID(), request.getConfig(), item.getId());
} }
batchMapper.insert(report); batchMapper.insert(report);
reportIds.add(group.getName()); reportIds.add(group.getName());
@ -1380,7 +1390,7 @@ public class ApiAutomationService {
String runMode = ApiRunMode.SCENARIO.name(); String runMode = ApiRunMode.SCENARIO.name();
if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) { if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) {
runMode = ApiRunMode.SCENARIO_PLAN.name(); runMode = ApiRunMode.SCENARIO_PLAN.name();
}else if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO.name())) { } else if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO.name())) {
runMode = ApiRunMode.SCHEDULE_SCENARIO.name(); runMode = ApiRunMode.SCHEDULE_SCENARIO.name();
} }
if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.DEFINITION.name())) { if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.DEFINITION.name())) {
@ -1490,7 +1500,7 @@ public class ApiAutomationService {
} }
APIScenarioReportResult report = createScenarioReport(request.getId(), request.getScenarioId(), request.getScenarioName(), ReportTriggerMode.MANUAL.name(), request.getExecuteType(), request.getProjectId(), APIScenarioReportResult report = createScenarioReport(request.getId(), request.getScenarioId(), request.getScenarioName(), ReportTriggerMode.MANUAL.name(), request.getExecuteType(), request.getProjectId(),
SessionUtils.getUserId(), request.getConfig(),request.getId()); SessionUtils.getUserId(), request.getConfig(), request.getId());
apiScenarioReportMapper.insert(report); apiScenarioReportMapper.insert(report);
uploadBodyFiles(request.getBodyFileRequestIds(), bodyFiles); uploadBodyFiles(request.getBodyFileRequestIds(), bodyFiles);
@ -2393,7 +2403,7 @@ public class ApiAutomationService {
example.createCriteria().andNameEqualTo(newModel.getName()). example.createCriteria().andNameEqualTo(newModel.getName()).
andProjectIdEqualTo(newModel.getProjectId()).andStatusNotEqualTo("Trash").andIdNotEqualTo(newModel.getId()); andProjectIdEqualTo(newModel.getProjectId()).andStatusNotEqualTo("Trash").andIdNotEqualTo(newModel.getId());
if (apiScenarioMapper.countByExample(example) > 0) { if (apiScenarioMapper.countByExample(example) > 0) {
stringBuffer.append(newModel.getName()+";"); stringBuffer.append(newModel.getName() + ";");
continue; continue;
} else { } else {
boolean insertFlag = true; boolean insertFlag = true;
@ -2426,11 +2436,11 @@ public class ApiAutomationService {
} }
BatchOperaResponse result = new BatchOperaResponse(); BatchOperaResponse result = new BatchOperaResponse();
if(stringBuffer.length() == 0){ if (stringBuffer.length() == 0) {
result.result = true; result.result = true;
}else { } else {
result.result = false; result.result = false;
result.errorMsg = stringBuffer.substring(0,stringBuffer.length()-1); result.errorMsg = stringBuffer.substring(0, stringBuffer.length() - 1);
} }
return result; return result;
} }
@ -2497,16 +2507,16 @@ public class ApiAutomationService {
public void initExecuteTimes() { public void initExecuteTimes() {
List<String> apiScenarioIds = extApiScenarioMapper.selectIdsByExecuteTimeIsNull(); List<String> apiScenarioIds = extApiScenarioMapper.selectIdsByExecuteTimeIsNull();
Map<String,Long> scenarioIdMap = new HashMap<>(); Map<String, Long> scenarioIdMap = new HashMap<>();
List<ApiReportCountDTO> reportCount = apiScenarioReportService.countByApiScenarioId(); List<ApiReportCountDTO> reportCount = apiScenarioReportService.countByApiScenarioId();
for (ApiReportCountDTO dto : reportCount) { for (ApiReportCountDTO dto : reportCount) {
scenarioIdMap.put(dto.getId(),dto.getCountNum()); scenarioIdMap.put(dto.getId(), dto.getCountNum());
} }
for (String id:apiScenarioIds) { for (String id : apiScenarioIds) {
int count = 0; int count = 0;
if(scenarioIdMap.containsKey(id)){ if (scenarioIdMap.containsKey(id)) {
Long countNum = scenarioIdMap.get(id); Long countNum = scenarioIdMap.get(id);
if(countNum != null){ if (countNum != null) {
count = countNum.intValue(); count = countNum.intValue();
} }
} }
@ -2519,9 +2529,9 @@ public class ApiAutomationService {
public long countExecuteTimesByProjectID(String projectId) { public long countExecuteTimesByProjectID(String projectId) {
Long result = extApiScenarioMapper.countExecuteTimesByProjectID(projectId); Long result = extApiScenarioMapper.countExecuteTimesByProjectID(projectId);
if(result == null){ if (result == null) {
return 0; return 0;
}else { } else {
return result.longValue(); return result.longValue();
} }
} }

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.automation.ApiScenarioRequest; import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.ReferenceDTO; import io.metersphere.api.dto.automation.ReferenceDTO;
import io.metersphere.api.dto.automation.RunModeConfig;
import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
@ -686,7 +687,9 @@ public class ApiDefinitionService {
// 调用执行方法 // 调用执行方法
if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) {
jMeterService.runTest(request.getId(), request.getId(), runMode, null, request.getConfig()); RunModeConfig configs = request.getConfig();
configs.setBaseInfo(CommonBeanFactory.getBean(SystemParameterService.class).getBaseInfo());
jMeterService.runTest(request.getId(), request.getId(), runMode, null, configs);
} else { } else {
jMeterService.runLocal(request.getId(), hashTree, request.getReportId(), runMode); jMeterService.runLocal(request.getId(), hashTree, request.getReportId(), runMode);
} }

View File

@ -3,7 +3,6 @@ package io.metersphere.api.service;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import io.metersphere.api.dto.definition.request.MsTestPlan; import io.metersphere.api.dto.definition.request.MsTestPlan;
import io.metersphere.api.dto.scenario.request.BodyFile; 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.ApiScenarioWithBLOBs;
import io.metersphere.base.domain.JarConfig; import io.metersphere.base.domain.JarConfig;
import io.metersphere.base.domain.TestPlanApiScenario; import io.metersphere.base.domain.TestPlanApiScenario;
@ -20,7 +19,6 @@ import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -31,7 +29,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@Service @Service
@Transactional(rollbackFor = Exception.class)
public class ApiJmeterFileService { public class ApiJmeterFileService {
@Resource @Resource
@ -42,10 +39,20 @@ public class ApiJmeterFileService {
private TestPlanApiScenarioMapper testPlanApiScenarioMapper; private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
@Resource @Resource
private ApiScenarioMapper apiScenarioMapper; private ApiScenarioMapper apiScenarioMapper;
@Resource
private JMeterService jMeterService;
public byte[] downloadJmeterFiles(String runMode, String testId, String reportId, String testPlanScenarioId) { public byte[] downloadJmeterFiles(List<BodyFile> bodyFileList) {
Map<String, byte[]> files = new LinkedHashMap<>();
Map<String, byte[]> multipartFiles = this.getMultipartFiles(bodyFileList);
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);
}
public byte[] downloadJmx(String runMode, String testId, String reportId, String testPlanScenarioId) {
Map<String, String> planEnvMap = new HashMap<>(); Map<String, String> planEnvMap = new HashMap<>();
if (StringUtils.isNotEmpty(testPlanScenarioId)) { if (StringUtils.isNotEmpty(testPlanScenarioId)) {
// 获取场景用例单独的执行环境 // 获取场景用例单独的执行环境
@ -66,7 +73,8 @@ public class ApiJmeterFileService {
hashTree = apiAutomationService.generateHashTree(item, reportId, planEnvMap); hashTree = apiAutomationService.generateHashTree(item, reportId, planEnvMap);
} }
//jMeterService.addBackendListener(reportId, hashTree); //jMeterService.addBackendListener(reportId, hashTree);
return zipFilesToByteArray(testId, hashTree); String jmx = new MsTestPlan().getJmx(hashTree);
return jmx.getBytes(StandardCharsets.UTF_8);
} }
public byte[] downloadJmeterJar() { public byte[] downloadJmeterJar() {
@ -124,6 +132,23 @@ public class ApiJmeterFileService {
return multipartFiles; return multipartFiles;
} }
private Map<String, byte[]> getMultipartFiles(List<BodyFile> files) {
Map<String, byte[]> multipartFiles = new LinkedHashMap<>();
// 获取附件
if (CollectionUtils.isNotEmpty(files)) {
for (BodyFile bodyFile : files) {
File file = new File(bodyFile.getName());
if (file != null && !file.exists()) {
byte[] fileByte = FileUtils.fileToByte(file);
if (fileByte != null) {
multipartFiles.put(file.getName(), fileByte);
}
}
}
}
return multipartFiles;
}
private byte[] zipFilesToByteArray(String testId, HashTree hashTree) { private byte[] zipFilesToByteArray(String testId, HashTree hashTree) {
String fileName = testId + ".jmx"; String fileName = testId + ".jmx";
String jmx = new MsTestPlan().getJmx(hashTree); String jmx = new MsTestPlan().getJmx(hashTree);
@ -141,6 +166,11 @@ public class ApiJmeterFileService {
return listBytesToZip(files); return listBytesToZip(files);
} }
private byte[] fileToByteArray(HashTree hashTree) {
String jmx = new MsTestPlan().getJmx(hashTree);
return jmx.getBytes(StandardCharsets.UTF_8);
}
private byte[] listBytesToZip(Map<String, byte[]> mapReport) { private byte[] listBytesToZip(Map<String, byte[]> mapReport) {
try { try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();

View File

@ -296,7 +296,7 @@ public class ApiScenarioReportService {
List<String> reportIds = new ArrayList<>(); List<String> reportIds = new ArrayList<>();
List<String> scenarioIdList = new ArrayList<>(); List<String> scenarioIdList = new ArrayList<>();
Map<String, String> scenarioAndErrorMap = new HashMap<>(); Map<String, String> scenarioAndErrorMap = new HashMap<>();
Map<String,APIScenarioReportResult> caseReportMap = new HashMap<>(); Map<String, APIScenarioReportResult> caseReportMap = new HashMap<>();
for (ScenarioResult scenarioResult : scenarioResultList) { for (ScenarioResult scenarioResult : scenarioResultList) {
// 存储场景报告 // 存储场景报告
@ -387,7 +387,7 @@ public class ApiScenarioReportService {
lastReport = report; lastReport = report;
APIScenarioReportResult reportResult = this.get(report.getId()); APIScenarioReportResult reportResult = this.get(report.getId());
caseReportMap.put(testPlanApiScenario.getApiScenarioId(),reportResult); caseReportMap.put(testPlanApiScenario.getApiScenarioId(), reportResult);
reportIds.add(report.getId()); reportIds.add(report.getId());
} }
TestPlanReportService testPlanReportService = CommonBeanFactory.getBean(TestPlanReportService.class); TestPlanReportService testPlanReportService = CommonBeanFactory.getBean(TestPlanReportService.class);
@ -395,8 +395,8 @@ public class ApiScenarioReportService {
testPlanLog.info("TestPlanReportId" + JSONArray.toJSONString(testPlanReportIdList) + " EXECUTE OVER. SCENARIO STATUS : " + JSONObject.toJSONString(scenarioAndErrorMap)); testPlanLog.info("TestPlanReportId" + JSONArray.toJSONString(testPlanReportIdList) + " EXECUTE OVER. SCENARIO STATUS : " + JSONObject.toJSONString(scenarioAndErrorMap));
for (String reportId : testPlanReportIdList) { for (String reportId : testPlanReportIdList) {
TestPlanReportExecuteCatch.updateApiTestPlanExecuteInfo(reportId,null,scenarioAndErrorMap,null); TestPlanReportExecuteCatch.updateApiTestPlanExecuteInfo(reportId, null, scenarioAndErrorMap, null);
TestPlanReportExecuteCatch.updateTestPlanExecuteResultInfo(reportId,null,caseReportMap,null); TestPlanReportExecuteCatch.updateTestPlanExecuteResultInfo(reportId, null, caseReportMap, null);
} }
return lastReport; return lastReport;
@ -550,7 +550,6 @@ public class ApiScenarioReportService {
public ApiScenarioReport updateScenario(TestResult result) { public ApiScenarioReport updateScenario(TestResult result) {
// 针对未正常返回结果的报告计数 // 针对未正常返回结果的报告计数
counter(result); counter(result);
ApiScenarioReport lastReport = null; ApiScenarioReport lastReport = null;
for (ScenarioResult item : result.getScenarios()) { for (ScenarioResult item : result.getScenarios()) {
// 更新报告状态 // 更新报告状态
@ -599,6 +598,7 @@ public class ApiScenarioReportService {
if (obj != null) { if (obj != null) {
ReportCounter counter = (ReportCounter) obj; ReportCounter counter = (ReportCounter) obj;
counter.setNumber(counter.getNumber() + 1); counter.setNumber(counter.getNumber() + 1);
System.out.println("得到统计数量:" + counter.getNumber());
MessageCache.cache.put(report.getScenarioId(), counter); MessageCache.cache.put(report.getScenarioId(), counter);
} }
} }
@ -623,7 +623,9 @@ public class ApiScenarioReportService {
} }
Map paramMap = new HashMap<>(beanMap); Map paramMap = new HashMap<>(beanMap);
paramMap.put("operator", SessionUtils.getUser().getName()); if (SessionUtils.getUser() != null) {
paramMap.put("operator", SessionUtils.getUser().getName());
}
paramMap.put("status", result.getLastResult()); paramMap.put("status", result.getLastResult());
String context = "${operator}执行接口自动化" + status + ": ${name}"; String context = "${operator}执行接口自动化" + status + ": ${name}";
NoticeModel noticeModel = NoticeModel.builder() NoticeModel noticeModel = NoticeModel.builder()

View File

@ -16,7 +16,6 @@ import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.SystemParameterService; import io.metersphere.service.SystemParameterService;
import io.metersphere.track.request.testcase.TrackCount; import io.metersphere.track.request.testcase.TrackCount;
import io.metersphere.track.service.TestPlanApiCaseService; import io.metersphere.track.service.TestPlanApiCaseService;
import io.metersphere.track.service.TestPlanReportService;
import io.metersphere.track.service.TestPlanScenarioCaseService; import io.metersphere.track.service.TestPlanScenarioCaseService;
import io.metersphere.track.service.TestPlanTestCaseService; import io.metersphere.track.service.TestPlanTestCaseService;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
@ -42,8 +41,6 @@ public class TestResultService {
@Resource @Resource
private ApiDefinitionExecResultService apiDefinitionExecResultService; private ApiDefinitionExecResultService apiDefinitionExecResultService;
@Resource @Resource
private TestPlanReportService testPlanReportService;
@Resource
private ApiScenarioReportService apiScenarioReportService; private ApiScenarioReportService apiScenarioReportService;
@Resource @Resource
private ApiTestCaseService apiTestCaseService; private ApiTestCaseService apiTestCaseService;
@ -158,6 +155,7 @@ public class TestResultService {
} }
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
LogUtil.error(e.getMessage(), e); LogUtil.error(e.getMessage(), e);
} }
} }

View File

@ -6,6 +6,7 @@ package io.metersphere.api.service.task;
import io.metersphere.api.dto.RunModeDataDTO; import io.metersphere.api.dto.RunModeDataDTO;
import io.metersphere.api.dto.automation.RunScenarioRequest; import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -26,10 +27,10 @@ public class ParallelScenarioExecTask<T> implements Callable<T> {
@Override @Override
public T call() { public T call() {
try { try {
if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { if (request.getConfig() != null && StringUtils.isNotEmpty(request.getConfig().getResourcePoolId())) {
jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReport().getId(), request.getRunMode(), request.getPlanScenarioId(), request.getConfig()); jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReport().getId(), request.getRunMode(), request.getPlanScenarioId(), request.getConfig());
} else { } else {
jMeterService.runLocal(runModeDataDTO.getReport().getId(), runModeDataDTO.getHashTree(), request.getReportId(), request.getRunMode()); jMeterService.runLocal(runModeDataDTO.getReport().getId(), runModeDataDTO.getHashTree(), TriggerMode.BATCH.name().equals(request.getTriggerMode()) ? TriggerMode.BATCH.name() : request.getReportId(), request.getRunMode());
} }
return null; return null;
} catch (Exception ex) { } catch (Exception ex) {

View File

@ -9,6 +9,7 @@ import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.base.domain.ApiScenarioReport; import io.metersphere.base.domain.ApiScenarioReport;
import io.metersphere.base.mapper.ApiScenarioReportMapper; import io.metersphere.base.mapper.ApiScenarioReportMapper;
import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -35,7 +36,7 @@ public class SerialScenarioExecTask<T> implements Callable<T> {
if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) { if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) {
jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReport().getId(), request.getRunMode(), request.getPlanScenarioId(), request.getConfig()); jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReport().getId(), request.getRunMode(), request.getPlanScenarioId(), request.getConfig());
} else { } else {
jMeterService.runLocal(runModeDataDTO.getReport().getId(), runModeDataDTO.getHashTree(), request.getReportId(), request.getRunMode()); jMeterService.runLocal(runModeDataDTO.getReport().getId(), runModeDataDTO.getHashTree(), TriggerMode.BATCH.name().equals(request.getTriggerMode()) ? TriggerMode.BATCH.name() : request.getReportId(), request.getRunMode());
} }
// 轮询查看报告状态最多200次防止死循环 // 轮询查看报告状态最多200次防止死循环
int index = 1; int index = 1;

View File

@ -38,6 +38,7 @@ public class ShiroUtils {
filterChainDefinitionMap.put("/sso/callback", "anon"); filterChainDefinitionMap.put("/sso/callback", "anon");
filterChainDefinitionMap.put("/license/valid", "anon"); filterChainDefinitionMap.put("/license/valid", "anon");
filterChainDefinitionMap.put("/api/jmeter/download", "anon"); filterChainDefinitionMap.put("/api/jmeter/download", "anon");
filterChainDefinitionMap.put("/api/jmeter/download/files", "anon");
filterChainDefinitionMap.put("/api/jmeter/download/jar", "anon"); filterChainDefinitionMap.put("/api/jmeter/download/jar", "anon");
// for swagger // for swagger

View File

@ -34,7 +34,7 @@ public class ParallelApiExecTask<T> implements Callable<T> {
if (config != null && StringUtils.isNotBlank(config.getResourcePoolId())) { if (config != null && StringUtils.isNotBlank(config.getResourcePoolId())) {
jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getApiCaseId(), runMode, null, config); jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getApiCaseId(), runMode, null, config);
} else { } else {
jMeterService.runLocal(runModeDataDTO.getApiCaseId(), runModeDataDTO.getHashTree(), null, runMode); jMeterService.runLocal(runModeDataDTO.getApiCaseId(), runModeDataDTO.getHashTree(), runModeDataDTO.getReport() != null ? runModeDataDTO.getReport().getTriggerMode() : null, runMode);
} }
return null; return null;
} catch (Exception ex) { } catch (Exception ex) {

View File

@ -36,7 +36,7 @@ public class SerialApiExecTask<T> implements Callable<T> {
if (config != null && StringUtils.isNotBlank(config.getResourcePoolId())) { if (config != null && StringUtils.isNotBlank(config.getResourcePoolId())) {
jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getApiCaseId(), runMode, null, config); jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getApiCaseId(), runMode, null, config);
} else { } else {
jMeterService.runLocal(runModeDataDTO.getApiCaseId(), runModeDataDTO.getHashTree(), null, runMode); jMeterService.runLocal(runModeDataDTO.getApiCaseId(), runModeDataDTO.getHashTree(), runModeDataDTO.getReport() != null ? runModeDataDTO.getReport().getTriggerMode() : null, runMode);
} }
// 轮询查看报告状态最多200次防止死循环 // 轮询查看报告状态最多200次防止死循环
ApiDefinitionExecResult report = null; ApiDefinitionExecResult report = null;

View File

@ -18,6 +18,7 @@
package org.apache.jmeter.threads; package org.apache.jmeter.threads;
import io.metersphere.api.jmeter.MsResultCollector; import io.metersphere.api.jmeter.MsResultCollector;
import org.apache.commons.collections.CollectionUtils;
import org.apache.jmeter.assertions.Assertion; import org.apache.jmeter.assertions.Assertion;
import org.apache.jmeter.assertions.AssertionResult; import org.apache.jmeter.assertions.AssertionResult;
import org.apache.jmeter.control.Controller; import org.apache.jmeter.control.Controller;
@ -67,7 +68,9 @@ public class JMeterThread implements Runnable, Interruptible {
private static final String TRUE = Boolean.toString(true); // i.e. "true" private static final String TRUE = Boolean.toString(true); // i.e. "true"
/** How often to check for shutdown during ramp-up, default 1000ms */ /**
* How often to check for shutdown during ramp-up, default 1000ms
*/
private static final int RAMPUP_GRANULARITY = private static final int RAMPUP_GRANULARITY =
JMeterUtils.getPropDefault("jmeterthread.rampup.granularity", 1000); // $NON-NLS-1$ JMeterUtils.getPropDefault("jmeterthread.rampup.granularity", 1000); // $NON-NLS-1$
@ -77,7 +80,7 @@ public class JMeterThread implements Runnable, Interruptible {
private static final float ONE_AS_FLOAT = 1.0f; private static final float ONE_AS_FLOAT = 1.0f;
private static final boolean APPLY_TIMER_FACTOR = Float.compare(TIMER_FACTOR,ONE_AS_FLOAT) != 0; private static final boolean APPLY_TIMER_FACTOR = Float.compare(TIMER_FACTOR, ONE_AS_FLOAT) != 0;
private final Controller threadGroupLoopController; private final Controller threadGroupLoopController;
@ -169,8 +172,7 @@ public class JMeterThread implements Runnable, Interruptible {
/** /**
* Enable the scheduler for this JMeterThread. * Enable the scheduler for this JMeterThread.
* *
* @param sche * @param sche flag whether the scheduler should be enabled
* flag whether the scheduler should be enabled
*/ */
public void setScheduled(boolean sche) { public void setScheduled(boolean sche) {
this.scheduler = sche; this.scheduler = sche;
@ -197,8 +199,7 @@ public class JMeterThread implements Runnable, Interruptible {
/** /**
* Set the EndTime for this Thread. * Set the EndTime for this Thread.
* *
* @param etime * @param etime the EndTime value.
* the EndTime value.
*/ */
public void setEndTime(long etime) { public void setEndTime(long etime) {
endTime = etime; endTime = etime;
@ -235,6 +236,7 @@ public class JMeterThread implements Runnable, Interruptible {
public void setThreadName(String threadName) { public void setThreadName(String threadName) {
this.threadName = threadName; this.threadName = threadName;
} }
@Override @Override
public void run() { public void run() {
// threadContext is not thread-safe, so keep within thread // threadContext is not thread-safe, so keep within thread
@ -258,7 +260,7 @@ public class JMeterThread implements Runnable, Interruptible {
&& threadContext.getTestLogicalAction() != TestLogicalAction.CONTINUE) { && threadContext.getTestLogicalAction() != TestLogicalAction.CONTINUE) {
log.debug("Start Next Thread Loop option is on, Last sample failed, starting next thread loop"); log.debug("Start Next Thread Loop option is on, Last sample failed, starting next thread loop");
} }
if(onErrorStartNextLoop && !lastSampleOk){ if (onErrorStartNextLoop && !lastSampleOk) {
triggerLoopLogicalActionOnParentControllers(sam, threadContext, JMeterThread::continueOnThreadLoop); triggerLoopLogicalActionOnParentControllers(sam, threadContext, JMeterThread::continueOnThreadLoop);
} else { } else {
switch (threadContext.getTestLogicalAction()) { switch (threadContext.getTestLogicalAction()) {
@ -278,8 +280,7 @@ public class JMeterThread implements Runnable, Interruptible {
threadContext.setTestLogicalAction(TestLogicalAction.CONTINUE); threadContext.setTestLogicalAction(TestLogicalAction.CONTINUE);
sam = null; sam = null;
setLastSampleOk(threadContext.getVariables(), true); setLastSampleOk(threadContext.getVariables(), true);
} } else {
else {
sam = threadGroupLoopController.next(); sam = threadGroupLoopController.next();
} }
} }
@ -297,8 +298,7 @@ public class JMeterThread implements Runnable, Interruptible {
log.info("Stopping Test: {}", e.toString()); log.info("Stopping Test: {}", e.toString());
} }
shutdownTest(); shutdownTest();
} } catch (JMeterStopTestNowException e) { // NOSONAR
catch (JMeterStopTestNowException e) { // NOSONAR
if (log.isInfoEnabled()) { if (log.isInfoEnabled()) {
log.info("Stopping Test Now: {}", e.toString()); log.info("Stopping Test Now: {}", e.toString());
} }
@ -328,12 +328,13 @@ public class JMeterThread implements Runnable, Interruptible {
/** /**
* Trigger break/continue/switch to next thread Loop depending on consumer implementation * Trigger break/continue/switch to next thread Loop depending on consumer implementation
* @param sampler Sampler Base sampler *
* @param sampler Sampler Base sampler
* @param threadContext * @param threadContext
* @param consumer Consumer that will process the tree of elements up to root node * @param consumer Consumer that will process the tree of elements up to root node
*/ */
private void triggerLoopLogicalActionOnParentControllers(Sampler sampler, JMeterContext threadContext, private void triggerLoopLogicalActionOnParentControllers(Sampler sampler, JMeterContext threadContext,
Consumer<FindTestElementsUpToRootTraverser> consumer) { Consumer<FindTestElementsUpToRootTraverser> consumer) {
TransactionSampler transactionSampler = null; TransactionSampler transactionSampler = null;
if (sampler instanceof TransactionSampler) { if (sampler instanceof TransactionSampler) {
transactionSampler = (TransactionSampler) sampler; transactionSampler = (TransactionSampler) sampler;
@ -343,7 +344,7 @@ public class JMeterThread implements Runnable, Interruptible {
if (realSampler == null) { if (realSampler == null) {
throw new IllegalStateException( throw new IllegalStateException(
"Got null subSampler calling findRealSampler for:" + "Got null subSampler calling findRealSampler for:" +
(sampler != null ? sampler.getName() : "null") + ", sampler:" + sampler); (sampler != null ? sampler.getName() : "null") + ", sampler:" + sampler);
} }
// Find parent controllers of current sampler // Find parent controllers of current sampler
FindTestElementsUpToRootTraverser pathToRootTraverser = new FindTestElementsUpToRootTraverser(realSampler); FindTestElementsUpToRootTraverser pathToRootTraverser = new FindTestElementsUpToRootTraverser(realSampler);
@ -364,6 +365,7 @@ public class JMeterThread implements Runnable, Interruptible {
/** /**
* Executes a continue of current loop, equivalent of "continue" in algorithm. * Executes a continue of current loop, equivalent of "continue" in algorithm.
* As a consequence it ends the first loop it finds on the path to root * As a consequence it ends the first loop it finds on the path to root
*
* @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser} * @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser}
*/ */
private static void continueOnCurrentLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) { private static void continueOnCurrentLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) {
@ -384,6 +386,7 @@ public class JMeterThread implements Runnable, Interruptible {
/** /**
* Executes a break of current loop, equivalent of "break" in algorithm. * Executes a break of current loop, equivalent of "break" in algorithm.
* As a consequence it ends the first loop it finds on the path to root * As a consequence it ends the first loop it finds on the path to root
*
* @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser} * @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser}
*/ */
private static void breakOnCurrentLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) { private static void breakOnCurrentLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) {
@ -404,6 +407,7 @@ public class JMeterThread implements Runnable, Interruptible {
/** /**
* Executes a restart of Thread loop, equivalent of "continue" in algorithm but on Thread Loop. * Executes a restart of Thread loop, equivalent of "continue" in algorithm but on Thread Loop.
* As a consequence it ends all loop on the path to root * As a consequence it ends all loop on the path to root
*
* @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser} * @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser}
*/ */
private static void continueOnThreadLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) { private static void continueOnThreadLoop(FindTestElementsUpToRootTraverser pathToRootTraverser) {
@ -424,6 +428,7 @@ public class JMeterThread implements Runnable, Interruptible {
* if there are some other controllers (SimpleController or other implementations) between this TransactionSampler and the real sampler, * if there are some other controllers (SimpleController or other implementations) between this TransactionSampler and the real sampler,
* triggerEndOfLoop will not be called for those controllers leaving them in "ugly" state. * triggerEndOfLoop will not be called for those controllers leaving them in "ugly" state.
* the following method will try to find the sampler that really generate an error * the following method will try to find the sampler that really generate an error
*
* @return {@link Sampler} * @return {@link Sampler}
*/ */
private Sampler findRealSampler(Sampler sampler) { private Sampler findRealSampler(Sampler sampler) {
@ -437,8 +442,8 @@ public class JMeterThread implements Runnable, Interruptible {
/** /**
* Process the current sampler, handling transaction samplers. * Process the current sampler, handling transaction samplers.
* *
* @param current sampler * @param current sampler
* @param parent sampler * @param parent sampler
* @param threadContext * @param threadContext
* @return SampleResult if a transaction was processed * @return SampleResult if a transaction was processed
*/ */
@ -519,8 +524,8 @@ public class JMeterThread implements Runnable, Interruptible {
} }
private void fillThreadInformation(SampleResult result, private void fillThreadInformation(SampleResult result,
int nbActiveThreadsInThreadGroup, int nbActiveThreadsInThreadGroup,
int nbTotalActiveThreads) { int nbTotalActiveThreads) {
result.setGroupThreads(nbActiveThreadsInThreadGroup); result.setGroupThreads(nbActiveThreadsInThreadGroup);
result.setAllThreads(nbTotalActiveThreads); result.setAllThreads(nbTotalActiveThreads);
result.setThreadName(threadName); result.setThreadName(threadName);
@ -531,9 +536,9 @@ public class JMeterThread implements Runnable, Interruptible {
* Broadcast the result to the sample listeners * Broadcast the result to the sample listeners
*/ */
private void executeSamplePackage(Sampler current, private void executeSamplePackage(Sampler current,
TransactionSampler transactionSampler, TransactionSampler transactionSampler,
SamplePackage transactionPack, SamplePackage transactionPack,
JMeterContext threadContext) { JMeterContext threadContext) {
threadContext.setCurrentSampler(current); threadContext.setCurrentSampler(current);
// Get the sampler ready to sample // Get the sampler ready to sample
@ -548,17 +553,18 @@ public class JMeterThread implements Runnable, Interruptible {
if (running) { if (running) {
Sampler sampler = pack.getSampler(); Sampler sampler = pack.getSampler();
// 执行前发给监听 // 执行前发给监听
List<SampleListener> sampleListeners = getSampleListeners(pack, transactionPack, transactionSampler); List<SampleListener> sampleListeners = pack.getSampleListeners();
SampleEvent event = new SampleEvent(null, current.getPropertyAsString("MS-RESOURCE-ID"), threadVars); if (CollectionUtils.isNotEmpty(sampleListeners)) {
for (SampleListener sampleListener : sampleListeners) { for (SampleListener sampleListener : sampleListeners) {
try { try {
if(sampleListener instanceof MsResultCollector) { if (sampleListener instanceof MsResultCollector) {
TestBeanHelper.prepare((TestElement) sampleListener); SampleEvent event = new SampleEvent(null, current.getPropertyAsString("MS-RESOURCE-ID"), threadVars);
sampleListener.sampleStarted(event); sampleListener.sampleStarted(event);
break; break;
}
} catch (RuntimeException e) {
log.error("自定义提前发送监听失败.", e);
} }
} catch (RuntimeException e) {
log.error("自定义提前发送监听失败.", e);
} }
} }
//====== //======
@ -622,8 +628,9 @@ public class JMeterThread implements Runnable, Interruptible {
* <li>Playing SampleMonitor before and after sampling</li> * <li>Playing SampleMonitor before and after sampling</li>
* <li>resetting currentSamplerForInterruption</li> * <li>resetting currentSamplerForInterruption</li>
* </ul> * </ul>
*
* @param threadContext {@link JMeterContext} * @param threadContext {@link JMeterContext}
* @param sampler {@link Sampler} * @param sampler {@link Sampler}
* @return {@link SampleResult} * @return {@link SampleResult}
*/ */
private SampleResult doSampling(JMeterContext threadContext, Sampler sampler) { private SampleResult doSampling(JMeterContext threadContext, Sampler sampler) {
@ -635,7 +642,7 @@ public class JMeterThread implements Runnable, Interruptible {
currentSamplerForInterruption = sampler; currentSamplerForInterruption = sampler;
if (!sampleMonitors.isEmpty()) { if (!sampleMonitors.isEmpty()) {
for (SampleMonitor sampleMonitor : sampleMonitors) { for (SampleMonitor sampleMonitor : sampleMonitors) {
if(sampleMonitor instanceof TestElement) { if (sampleMonitor instanceof TestElement) {
TestBeanHelper.prepare((TestElement) sampleMonitor); TestBeanHelper.prepare((TestElement) sampleMonitor);
} }
sampleMonitor.sampleStarting(sampler); sampleMonitor.sampleStarting(sampler);
@ -682,20 +689,20 @@ public class JMeterThread implements Runnable, Interruptible {
private List<SampleListener> getSampleListeners(SamplePackage samplePack, SamplePackage transactionPack, TransactionSampler transactionSampler) { private List<SampleListener> getSampleListeners(SamplePackage samplePack, SamplePackage transactionPack, TransactionSampler transactionSampler) {
List<SampleListener> sampleListeners = samplePack.getSampleListeners(); List<SampleListener> sampleListeners = samplePack.getSampleListeners();
// Do not send subsamples to listeners which receive the transaction sample // Do not send subsamples to listeners which receive the transaction sample
if(transactionSampler != null) { if (transactionSampler != null) {
List<SampleListener> onlySubSamplerListeners = new ArrayList<>(); List<SampleListener> onlySubSamplerListeners = new ArrayList<>();
List<SampleListener> transListeners = transactionPack.getSampleListeners(); List<SampleListener> transListeners = transactionPack.getSampleListeners();
for(SampleListener listener : sampleListeners) { for (SampleListener listener : sampleListeners) {
// Check if this instance is present in transaction listener list // Check if this instance is present in transaction listener list
boolean found = false; boolean found = false;
for(SampleListener trans : transListeners) { for (SampleListener trans : transListeners) {
// Check for the same instance // Check for the same instance
if(trans == listener) { if (trans == listener) {
found = true; found = true;
break; break;
} }
} }
if(!found) { if (!found) {
onlySubSamplerListeners.add(listener); onlySubSamplerListeners.add(listener);
} }
} }
@ -751,7 +758,7 @@ public class JMeterThread implements Runnable, Interruptible {
private void threadStarted() { private void threadStarted() {
JMeterContextService.incrNumberOfThreads(); JMeterContextService.incrNumberOfThreads();
threadGroup.incrNumberOfThreads(); threadGroup.incrNumberOfThreads();
GuiPackage gp =GuiPackage.getInstance(); GuiPackage gp = GuiPackage.getInstance();
if (gp != null) {// check there is a GUI if (gp != null) {// check there is a GUI
gp.getMainFrame().updateCounts(); gp.getMainFrame().updateCounts();
} }
@ -765,7 +772,7 @@ public class JMeterThread implements Runnable, Interruptible {
JMeterContextService.decrNumberOfThreads(); JMeterContextService.decrNumberOfThreads();
threadGroup.decrNumberOfThreads(); threadGroup.decrNumberOfThreads();
GuiPackage gp = GuiPackage.getInstance(); GuiPackage gp = GuiPackage.getInstance();
if (gp != null){// check there is a GUI if (gp != null) {// check there is a GUI
gp.getMainFrame().updateCounts(); gp.getMainFrame().updateCounts();
} }
if (iterationListener != null) { // probably not possible, but check anyway if (iterationListener != null) { // probably not possible, but check anyway
@ -826,18 +833,20 @@ public class JMeterThread implements Runnable, Interruptible {
log.info("Stopping: {}", threadName); log.info("Stopping: {}", threadName);
} }
/** {@inheritDoc} */ /**
* {@inheritDoc}
*/
@Override @Override
public boolean interrupt(){ public boolean interrupt() {
interruptLock.lock(); interruptLock.lock();
try { try {
Sampler samp = currentSamplerForInterruption; // fetch once; must be done under lock Sampler samp = currentSamplerForInterruption; // fetch once; must be done under lock
if (samp instanceof Interruptible){ // (also protects against null) if (samp instanceof Interruptible) { // (also protects against null)
if (log.isWarnEnabled()) { if (log.isWarnEnabled()) {
log.warn("Interrupting: {} sampler: {}", threadName, samp.getName()); log.warn("Interrupting: {} sampler: {}", threadName, samp.getName());
} }
try { try {
boolean found = ((Interruptible)samp).interrupt(); boolean found = ((Interruptible) samp).interrupt();
if (!found) { if (!found) {
log.warn("No operation pending"); log.warn("No operation pending");
} }