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

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

View File

@ -1,15 +1,15 @@
package io.metersphere.api.controller;
import io.metersphere.api.dto.scenario.request.BodyFile;
import io.metersphere.api.service.ApiJmeterFileService;
import org.apache.dubbo.common.utils.CollectionUtils;
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 org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.UUID;
@RestController
@ -19,15 +19,23 @@ public class ApiJmeterFileController {
@Resource
private ApiJmeterFileService apiJmeterFileService;
@GetMapping("download")
public ResponseEntity<byte[]> 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);
@PostMapping("download/files")
public ResponseEntity<byte[]> downloadJmeterFiles(@RequestBody List<BodyFile> bodyFileList) {
byte[] bytes = new byte[10];
if (CollectionUtils.isNotEmpty(bodyFileList)) {
bytes = apiJmeterFileService.downloadJmeterFiles(bodyFileList);
}
return ResponseEntity.ok()
.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);
}
@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")
public ResponseEntity<byte[]> downloadJmeterFiles() {
byte[] bytes = apiJmeterFileService.downloadJmeterJar();

View File

@ -1,8 +1,11 @@
package io.metersphere.api.dto.automation;
import io.metersphere.api.dto.JvmInfoDTO;
import io.metersphere.dto.BaseSystemConfigDTO;
import lombok.Data;
import java.util.Map;
import java.util.List;
@Data
public class RunModeConfig {
@ -12,7 +15,8 @@ public class RunModeConfig {
private String reportId;
private boolean onSampleError;
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 org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.samplers.SampleResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Comparator;
@ -20,8 +18,6 @@ import java.util.Map;
* 获取结果和数据库操作分离
* 减少占用的数据库连接
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class APIBackendListenerHandler {
@Resource

View File

@ -1,6 +1,7 @@
package io.metersphere.api.jmeter;
import com.alibaba.fastjson.JSON;
import io.metersphere.api.dto.JvmInfoDTO;
import io.metersphere.api.dto.RunRequest;
import io.metersphere.api.dto.automation.ExecuteType;
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.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ResourcePoolTypeEnum;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.*;
import io.metersphere.config.JmeterProperties;
import io.metersphere.dto.BaseSystemConfigDTO;
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.EngineFactory;
import io.metersphere.service.SystemParameterService;
import org.apache.commons.collections.CollectionUtils;
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.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
@ -35,9 +44,9 @@ import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.io.InputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
@ -128,17 +137,21 @@ public class JMeterService {
init();
FixedTask.tasks.put(testId, System.currentTimeMillis());
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);
}
LocalRunner runner = new LocalRunner(testPlan);
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();
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.setTestId(testId);
runRequest.setReportId(reportId);
@ -166,15 +179,24 @@ public class JMeterService {
MSException.throwException(e.getMessage());
}
} 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();
NodeDTO node = JSON.parseObject(configuration, NodeDTO.class);
String nodeIp = node.getIp();
Integer port = node.getPort();
try {
String uri = String.format(BASE_URL + "/jmeter/api/start", nodeIp, port);
ResponseEntity<String> result = restTemplate.postForEntity(uri, runRequest, String.class);
if (result == null || !StringUtils.equals("SUCCESS", result.getBody())) {
ResponseEntity<String> resultEntity = restTemplate.postForEntity(uri, runRequest, String.class);
String result = resultEntity.getBody(); // this.send(uri, runRequest);
if (StringUtils.isEmpty(result) || !StringUtils.equals("SUCCESS", result)) {
// 清理零时报告
ApiScenarioReportService apiScenarioReportService = CommonBeanFactory.getBean(ApiScenarioReportService.class);
apiScenarioReportService.delete(reportId);
@ -182,7 +204,31 @@ public class JMeterService {
}
} catch (Exception e) {
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.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
@Transactional(rollbackFor = Exception.class)
public class MsKafkaListener {
public static final String TOPICS = "ms-api-exec-topic";
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}")
public void consume(ConsumerRecord<?, String> record) {
LogUtil.info("接收到执行结果开始存储");
try {
this.save(record.value());
} catch (Exception e) {
LogUtil.error(e.getMessage());
}
LogUtil.info("执行内容存储结束");
}

View File

@ -16,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@ -41,7 +42,7 @@ public class ResourcePoolCalculation {
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andIdEqualTo(resourcePoolId);
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
// 按照NODE节点的可用内存空间大小排序
JvmInfoDTO jvmInfoDTO = null;
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();
@ -57,19 +58,49 @@ public class ResourcePoolCalculation {
if (nodeJvm == null) {
continue;
}
// 优先取资源充足的节点如果当前节点资源超过1G就不需要在排序了
if (nodeJvm.getVmFree() > 1024) {
return testResource;
}
if (jvmInfoDTO == null || jvmInfoDTO.getVmFree() < nodeJvm.getVmFree()) {
jvmInfoDTO = nodeJvm;
jvmInfoDTO.setTestResource(testResource);
nodeJvm.setTestResource(testResource);
availableNodes.add(nodeJvm);
}
}
}
if (jvmInfoDTO == null || jvmInfoDTO.getTestResource() == null) {
if (CollectionUtils.isEmpty(availableNodes)) {
MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】");
}
int index = (int) (Math.random() * availableNodes.size());
JvmInfoDTO jvmInfoDTO = availableNodes.get(index);
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.MessageCache;
import io.metersphere.api.jmeter.ReportCounter;
import io.metersphere.api.jmeter.ResourcePoolCalculation;
import io.metersphere.api.parse.ApiImportParser;
import io.metersphere.api.service.task.ParallelScenarioExecTask;
import io.metersphere.api.service.task.SerialScenarioExecTask;
@ -129,6 +130,8 @@ public class ApiAutomationService {
private TcpApiParamService tcpApiParamService;
@Resource
private ApiScenarioReferenceIdService apiScenarioReferenceIdService;
@Resource
private ResourcePoolCalculation resourcePoolCalculation;
public ApiScenarioWithBLOBs getDto(String id) {
return apiScenarioMapper.selectByPrimaryKey(id);
@ -989,6 +992,11 @@ public class ApiAutomationService {
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")));
}
// 资源池
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);
// 集合报告设置
@ -1087,7 +1095,7 @@ public class ApiAutomationService {
private void run(Map<String, RunModeDataDTO> executeQueue, RunScenarioRequest request, String serialReportId) {
// 开始选择执行模式
if (executeQueue != null && executeQueue.size() > 0) {
ExecutorService executorService = Executors.newFixedThreadPool(executeQueue.size());
ExecutorService executorService = Executors.newFixedThreadPool(6);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiScenarioReportMapper batchMapper = sqlSession.getMapper(ApiScenarioReportMapper.class);
if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) {
@ -1175,9 +1183,11 @@ public class ApiAutomationService {
//存储报告
APIScenarioReportResult report = executeQueue.get(reportId).getReport();
batchMapper.insert(report);
executorService.submit(new ParallelScenarioExecTask(jMeterService, executeQueue.get(reportId), request));
}
sqlSession.flushStatements();
for (String reportId : executeQueue.keySet()) {
executorService.submit(new ParallelScenarioExecTask(jMeterService, executeQueue.get(reportId), request));
}
}
}
}

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
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.definition.*;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
@ -686,7 +687,9 @@ public class ApiDefinitionService {
// 调用执行方法
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 {
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 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;
@ -20,7 +19,6 @@ import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jorphan.collections.HashTree;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
@ -31,7 +29,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiJmeterFileService {
@Resource
@ -42,10 +39,20 @@ public class ApiJmeterFileService {
private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
@Resource
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<>();
if (StringUtils.isNotEmpty(testPlanScenarioId)) {
// 获取场景用例单独的执行环境
@ -66,7 +73,8 @@ public class ApiJmeterFileService {
hashTree = apiAutomationService.generateHashTree(item, reportId, planEnvMap);
}
//jMeterService.addBackendListener(reportId, hashTree);
return zipFilesToByteArray(testId, hashTree);
String jmx = new MsTestPlan().getJmx(hashTree);
return jmx.getBytes(StandardCharsets.UTF_8);
}
public byte[] downloadJmeterJar() {
@ -124,6 +132,23 @@ public class ApiJmeterFileService {
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) {
String fileName = testId + ".jmx";
String jmx = new MsTestPlan().getJmx(hashTree);
@ -141,6 +166,11 @@ public class ApiJmeterFileService {
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) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();

View File

@ -550,7 +550,6 @@ public class ApiScenarioReportService {
public ApiScenarioReport updateScenario(TestResult result) {
// 针对未正常返回结果的报告计数
counter(result);
ApiScenarioReport lastReport = null;
for (ScenarioResult item : result.getScenarios()) {
// 更新报告状态
@ -599,6 +598,7 @@ public class ApiScenarioReportService {
if (obj != null) {
ReportCounter counter = (ReportCounter) obj;
counter.setNumber(counter.getNumber() + 1);
System.out.println("得到统计数量:" + counter.getNumber());
MessageCache.cache.put(report.getScenarioId(), counter);
}
}
@ -623,7 +623,9 @@ public class ApiScenarioReportService {
}
Map paramMap = new HashMap<>(beanMap);
if (SessionUtils.getUser() != null) {
paramMap.put("operator", SessionUtils.getUser().getName());
}
paramMap.put("status", result.getLastResult());
String context = "${operator}执行接口自动化" + status + ": ${name}";
NoticeModel noticeModel = NoticeModel.builder()

View File

@ -16,7 +16,6 @@ import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.SystemParameterService;
import io.metersphere.track.request.testcase.TrackCount;
import io.metersphere.track.service.TestPlanApiCaseService;
import io.metersphere.track.service.TestPlanReportService;
import io.metersphere.track.service.TestPlanScenarioCaseService;
import io.metersphere.track.service.TestPlanTestCaseService;
import org.apache.commons.collections4.CollectionUtils;
@ -42,8 +41,6 @@ public class TestResultService {
@Resource
private ApiDefinitionExecResultService apiDefinitionExecResultService;
@Resource
private TestPlanReportService testPlanReportService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
@Resource
private ApiTestCaseService apiTestCaseService;
@ -158,6 +155,7 @@ public class TestResultService {
}
}
} catch (Exception e) {
e.printStackTrace();
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.automation.RunScenarioRequest;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.lang3.StringUtils;
@ -26,10 +27,10 @@ public class ParallelScenarioExecTask<T> implements Callable<T> {
@Override
public T call() {
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());
} 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;
} 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.mapper.ApiScenarioReportMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
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())) {
jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getReport().getId(), request.getRunMode(), request.getPlanScenarioId(), request.getConfig());
} 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次防止死循环
int index = 1;

View File

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

View File

@ -34,7 +34,7 @@ public class ParallelApiExecTask<T> implements Callable<T> {
if (config != null && StringUtils.isNotBlank(config.getResourcePoolId())) {
jMeterService.runTest(runModeDataDTO.getTestId(), runModeDataDTO.getApiCaseId(), runMode, null, config);
} 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;
} catch (Exception ex) {

View File

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

View File

@ -18,6 +18,7 @@
package org.apache.jmeter.threads;
import io.metersphere.api.jmeter.MsResultCollector;
import org.apache.commons.collections.CollectionUtils;
import org.apache.jmeter.assertions.Assertion;
import org.apache.jmeter.assertions.AssertionResult;
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"
/** 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 =
JMeterUtils.getPropDefault("jmeterthread.rampup.granularity", 1000); // $NON-NLS-1$
@ -169,8 +172,7 @@ public class JMeterThread implements Runnable, Interruptible {
/**
* Enable the scheduler for this JMeterThread.
*
* @param sche
* flag whether the scheduler should be enabled
* @param sche flag whether the scheduler should be enabled
*/
public void setScheduled(boolean sche) {
this.scheduler = sche;
@ -197,8 +199,7 @@ public class JMeterThread implements Runnable, Interruptible {
/**
* Set the EndTime for this Thread.
*
* @param etime
* the EndTime value.
* @param etime the EndTime value.
*/
public void setEndTime(long etime) {
endTime = etime;
@ -235,6 +236,7 @@ public class JMeterThread implements Runnable, Interruptible {
public void setThreadName(String threadName) {
this.threadName = threadName;
}
@Override
public void run() {
// threadContext is not thread-safe, so keep within thread
@ -278,8 +280,7 @@ public class JMeterThread implements Runnable, Interruptible {
threadContext.setTestLogicalAction(TestLogicalAction.CONTINUE);
sam = null;
setLastSampleOk(threadContext.getVariables(), true);
}
else {
} else {
sam = threadGroupLoopController.next();
}
}
@ -297,8 +298,7 @@ public class JMeterThread implements Runnable, Interruptible {
log.info("Stopping Test: {}", e.toString());
}
shutdownTest();
}
catch (JMeterStopTestNowException e) { // NOSONAR
} catch (JMeterStopTestNowException e) { // NOSONAR
if (log.isInfoEnabled()) {
log.info("Stopping Test Now: {}", e.toString());
}
@ -328,6 +328,7 @@ public class JMeterThread implements Runnable, Interruptible {
/**
* Trigger break/continue/switch to next thread Loop depending on consumer implementation
*
* @param sampler Sampler Base sampler
* @param threadContext
* @param consumer Consumer that will process the tree of elements up to root node
@ -364,6 +365,7 @@ public class JMeterThread implements Runnable, Interruptible {
/**
* 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
*
* @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser}
*/
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.
* As a consequence it ends the first loop it finds on the path to root
*
* @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser}
*/
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.
* As a consequence it ends all loop on the path to root
*
* @param pathToRootTraverser {@link FindTestElementsUpToRootTraverser}
*/
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,
* 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
*
* @return {@link Sampler}
*/
private Sampler findRealSampler(Sampler sampler) {
@ -548,12 +553,12 @@ public class JMeterThread implements Runnable, Interruptible {
if (running) {
Sampler sampler = pack.getSampler();
// 执行前发给监听
List<SampleListener> sampleListeners = getSampleListeners(pack, transactionPack, transactionSampler);
SampleEvent event = new SampleEvent(null, current.getPropertyAsString("MS-RESOURCE-ID"), threadVars);
List<SampleListener> sampleListeners = pack.getSampleListeners();
if (CollectionUtils.isNotEmpty(sampleListeners)) {
for (SampleListener sampleListener : sampleListeners) {
try {
if (sampleListener instanceof MsResultCollector) {
TestBeanHelper.prepare((TestElement) sampleListener);
SampleEvent event = new SampleEvent(null, current.getPropertyAsString("MS-RESOURCE-ID"), threadVars);
sampleListener.sampleStarted(event);
break;
}
@ -561,6 +566,7 @@ public class JMeterThread implements Runnable, Interruptible {
log.error("自定义提前发送监听失败.", e);
}
}
}
//======
result = doSampling(threadContext, sampler);
}
@ -622,6 +628,7 @@ public class JMeterThread implements Runnable, Interruptible {
* <li>Playing SampleMonitor before and after sampling</li>
* <li>resetting currentSamplerForInterruption</li>
* </ul>
*
* @param threadContext {@link JMeterContext}
* @param sampler {@link Sampler}
* @return {@link SampleResult}
@ -826,7 +833,9 @@ public class JMeterThread implements Runnable, Interruptible {
log.info("Stopping: {}", threadName);
}
/** {@inheritDoc} */
/**
* {@inheritDoc}
*/
@Override
public boolean interrupt() {
interruptLock.lock();