refactor(接口测试): 优化异常终止报告处理

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2023-04-23 11:54:25 +08:00 committed by fit2-zhao
parent ff40cd438c
commit 46dce5a079
10 changed files with 121 additions and 82 deletions

View File

@ -11,6 +11,7 @@ import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.dto.RunModeConfigDTO;
import io.metersphere.service.RemakeReportService;
import io.metersphere.service.SystemParameterService;
import io.metersphere.utils.LoggerUtil;
import io.metersphere.vo.BooleanPool;
@ -20,6 +21,7 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@ -31,7 +33,8 @@ public class ApiCaseParallelExecuteService {
private JMeterService jMeterService;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private RemakeReportService remakeReportService;
public void parallel(Map<String, ApiDefinitionExecResultWithBLOBs> executeQueue, RunModeConfigDTO config, DBTestQueue executionQueue, String runMode) {
BooleanPool pool = GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId());
// 初始化分配策略
@ -47,30 +50,36 @@ public class ApiCaseParallelExecuteService {
ApiDefinitionExecResultWithBLOBs result = executeQueue.get(testId);
String reportId = result.getId();
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testId, reportId, runMode);
runRequest.setPool(pool);
runRequest.setTestPlanReportId(executionQueue.getReportId());
runRequest.setPoolId(config.getResourcePoolId());
runRequest.setReportType(executionQueue.getReportType());
runRequest.setRunType(RunModeConstants.PARALLEL.toString());
runRequest.setQueueId(executionQueue.getId());
try {
runRequest.setPool(pool);
runRequest.setTestPlanReportId(executionQueue.getReportId());
runRequest.setPoolId(config.getResourcePoolId());
runRequest.setReportType(executionQueue.getReportType());
runRequest.setRunType(RunModeConstants.PARALLEL.toString());
runRequest.setQueueId(executionQueue.getId());
runRequest.setRetryNum(config.getRetryNum());
runRequest.setRetryEnable(config.isRetryEnable());
runRequest.setRetryNum(config.getRetryNum());
runRequest.setRetryEnable(config.isRetryEnable());
Map<String, Object> extendedParameters = new HashMap<>();
extendedParameters.put(CommonConstants.USER_ID, result.getUserId());
runRequest.setExtendedParameters(extendedParameters);
if (MapUtils.isNotEmpty(executionQueue.getDetailMap())) {
runRequest.setPlatformUrl(GenerateHashTreeUtil.getPlatformUrl(baseInfo, runRequest, executionQueue.getDetailMap().get(result.getId())));
Map<String, Object> extendedParameters = new HashMap<>();
extendedParameters.put(CommonConstants.USER_ID, result.getUserId());
runRequest.setExtendedParameters(extendedParameters);
if (MapUtils.isNotEmpty(executionQueue.getDetailMap())) {
runRequest.setPlatformUrl(GenerateHashTreeUtil.getPlatformUrl(baseInfo, runRequest, executionQueue.getDetailMap().get(result.getId())));
}
if (!pool.isPool()) {
HashTree hashTree = apiCaseSerialService.generateHashTree(testId, config.getEnvMap(), runRequest);
runRequest.setHashTree(hashTree);
}
// 开始执行
runRequest.getExtendedParameters().put("projectId", executionQueue.getDetail().getProjectIds());
LoggerUtil.info("进入并行模式,开始执行用例:[" + result.getName() + "] 报告ID [" + reportId + "]");
} catch (Exception e) {
remakeReportService.testEnded(runRequest, e.getMessage());
LoggerUtil.error("脚本处理失败", runRequest.getReportId(), e);
continue;
}
if (!pool.isPool()) {
HashTree hashTree = apiCaseSerialService.generateHashTree(testId, config.getEnvMap(), runRequest);
runRequest.setHashTree(hashTree);
}
// 开始执行
runRequest.getExtendedParameters().put("projectId", executionQueue.getDetail().getProjectIds());
LoggerUtil.info("进入并行模式,开始执行用例:[" + result.getName() + "] 报告ID [" + reportId + "]");
jMeterService.run(runRequest);
}
}

View File

@ -20,6 +20,7 @@ import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.constants.PropertyConstant;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.JmeterRunRequestDTO;
@ -54,15 +55,12 @@ public class ApiCaseSerialService {
private RedisTemplate<String, Object> redisTemplate;
@Resource
private TestPlanApiCaseMapper testPlanApiCaseMapper;
@Resource
private RemakeReportService remakeReportService;
public void serial(DBTestQueue executionQueue) {
ApiExecutionQueueDetail queue = executionQueue.getDetail();
JmeterRunRequestDTO runRequest = RequestParamsUtil.init(executionQueue, queue, queue.getReportId());
// 判断触发资源对象是用例
if (!GenerateHashTreeUtil.isSetReport(executionQueue.getReportType())
|| StringUtils.equalsIgnoreCase(executionQueue.getRunMode(), ApiRunMode.DEFINITION.name())) {
updateDefinitionExecResultToRunning(queue, runRequest);
}
try {
if (StringUtils.isEmpty(executionQueue.getPoolId())) {
Map<String, String> map = new LinkedHashMap<>();
@ -81,10 +79,17 @@ public class ApiCaseSerialService {
}
// 开始执行
runRequest.getExtendedParameters().put(PROJECT_ID, queue.getProjectIds());
jMeterService.run(runRequest);
} catch (Exception e) {
LoggerUtil.error("串行执行用例失败", e);
LoggerUtil.error("脚本处理失败", runRequest.getReportId(), e);
remakeReportService.testEnded(runRequest, e.getMessage());
return;
}
// 判断触发资源对象是用例
if (!GenerateHashTreeUtil.isSetReport(executionQueue.getReportType())
|| StringUtils.equalsIgnoreCase(executionQueue.getRunMode(), ApiRunMode.DEFINITION.name())) {
updateDefinitionExecResultToRunning(queue, runRequest);
}
jMeterService.run(runRequest);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@ -158,9 +163,9 @@ public class ApiCaseSerialService {
return jmeterHashTree;
}
} catch (Exception ex) {
RemakeReportService remakeReportService = CommonBeanFactory.getBean(RemakeReportService.class);
remakeReportService.testEnded(runRequest, ex.getMessage());
LoggerUtil.error("用例资源:" + testId + ", 生成执行脚本失败", runRequest.getReportId(), ex);
String errorMsg = StringUtils.join("用例资源:", testId + ", 生成执行脚本失败");
LoggerUtil.error(errorMsg, runRequest.getReportId(), ex);
MSException.throwException(errorMsg);
}
return null;
}

View File

@ -150,7 +150,7 @@ public class ApiExecuteService {
}
jMeterService.run(runRequest);
} catch (Exception ex) {
remakeReportService.testEnded(runRequest, ex.getMessage());
remakeReportService.updateReport(runRequest, ex.getMessage());
LogUtil.error(ex.getMessage(), ex);
}
}

View File

@ -28,6 +28,7 @@ public class KubernetesTestEngine extends AbstractEngine {
private final String DEBUG_ERROR = "DEBUG_ERROR";
private final String EXEC_URL = "api/start";
private final String DEBUG_URL = "debug";
private final String LOCAL_URL = "http://127.0.0.1:8082/jmeter/";
// 初始化API调用
public KubernetesTestEngine(JmeterRunRequestDTO runRequest) {
@ -53,6 +54,7 @@ public class KubernetesTestEngine extends AbstractEngine {
}
private void runApi(TestResource resource) {
boolean isDebug = runRequest.getHashTree() != null;
try {
ClientCredential clientCredential = JSON.parseObject(resource.getConfiguration(), ClientCredential.class);
KubernetesProvider kubernetesProvider = ConstructorUtils.invokeConstructor(providerClass, clientCredential);
@ -65,7 +67,6 @@ public class KubernetesTestEngine extends AbstractEngine {
.append(JSON.toJSONString(pod.getMetadata())).append("");
LoggerUtil.info(logMsg);
boolean isDebug = runRequest.getHashTree() != null;
if (isDebug) {
ElementUtil.coverArguments(runRequest.getHashTree());
if (runRequest.isDebug() && !StringUtils.equalsAny(runRequest.getRunMode(), ApiRunMode.DEFINITION.name())) {
@ -83,20 +84,23 @@ public class KubernetesTestEngine extends AbstractEngine {
command.append(StringUtils.SPACE).append("--connect-timeout 30"); // 设置连接超时时间为30S
command.append(StringUtils.SPACE).append("--max-time 120"); // 设置请求超时时间为120S
command.append(StringUtils.SPACE).append("--retry 3"); // 设置重试次数3次
command.append(StringUtils.SPACE).append("http://127.0.0.1:8082/jmeter/").append(isDebug ? DEBUG_URL : EXEC_URL);
command.append(StringUtils.SPACE).append(LOCAL_URL).append(isDebug ? DEBUG_URL : EXEC_URL);
KubernetesApiExec.newExecWatch(client, clientCredential.getNamespace(), pod.getMetadata().getName(), command.toString(), runRequest);
} catch (Exception e) {
MsgDTO dto = new MsgDTO();
dto.setExecEnd(false);
dto.setContent(DEBUG_ERROR);
dto.setReportId("send." + runRequest.getReportId());
dto.setToReport(runRequest.getReportId());
LoggerUtil.debug("send. " + runRequest.getReportId());
WebSocketUtil.sendMessageSingle(dto);
WebSocketUtil.onClose(runRequest.getReportId());
RemakeReportService remake = CommonBeanFactory.getBean(RemakeReportService.class);
remake.testEnded(runRequest, StringUtils.join("K8s执行异常", e.getMessage()));
if (isDebug) {
MsgDTO dto = new MsgDTO();
dto.setExecEnd(false);
dto.setContent(DEBUG_ERROR);
dto.setReportId("send." + runRequest.getReportId());
dto.setToReport(runRequest.getReportId());
LoggerUtil.debug("send. " + runRequest.getReportId());
WebSocketUtil.sendMessageSingle(dto);
WebSocketUtil.onClose(runRequest.getReportId());
remake.updateReport(runRequest, StringUtils.join("K8s执行异常", e.getMessage()));
}else {
remake.testEnded(runRequest, StringUtils.join("K8s执行异常", e.getMessage()));
}
LoggerUtil.error("当前报告:【" + runRequest.getReportId() + "】资源:【" + runRequest.getTestId() + "】CURL失败", e);
}
}

View File

@ -3,21 +3,22 @@ package io.metersphere.api.exec.scenario;
import io.metersphere.api.dto.RunModeDataDTO;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.commons.utils.GenerateHashTreeUtil;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.utils.SmoothWeighted;
import io.metersphere.commons.utils.GenerateHashTreeUtil;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.service.RemakeReportService;
import io.metersphere.service.SystemParameterService;
import io.metersphere.utils.LoggerUtil;
import io.metersphere.vo.BooleanPool;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.util.Map;
@Service
@ -29,6 +30,8 @@ public class ApiScenarioParallelService {
protected SystemParameterService systemParameterService;
@Resource
protected RedisTemplate<String, Object> redisTemplate;
@Resource
private RemakeReportService remakeReportService;
public void parallel(Map<String, RunModeDataDTO> executeQueue, RunScenarioRequest request, String serialReportId, DBTestQueue executionQueue) {
// 初始化分配策略
@ -43,16 +46,24 @@ public class ApiScenarioParallelService {
break;
}
RunModeDataDTO dataDTO = executeQueue.get(reportId);
JmeterRunRequestDTO runRequest = getJmeterRunRequestDTO(request, serialReportId, executionQueue, baseInfo, reportId, dataDTO);
runRequest.setPool(pool);
runRequest.setPoolId(request.getConfig().getResourcePoolId());
// 本地执行生成hashTree
if (!pool.isPool()) {
runRequest.setHashTree(GenerateHashTreeUtil.generateHashTree(dataDTO.getScenario(), dataDTO.getPlanEnvMap(), runRequest));
JmeterRunRequestDTO runRequest = getJmeterRunRequestDTO(request, serialReportId,
executionQueue, baseInfo, reportId, dataDTO);
try {
runRequest.setPool(pool);
runRequest.setPoolId(request.getConfig().getResourcePoolId());
// 本地执行生成hashTree
if (!pool.isPool()) {
runRequest.setHashTree(GenerateHashTreeUtil.generateHashTree(dataDTO.getScenario(),
dataDTO.getPlanEnvMap(), runRequest));
}
runRequest.getExtendedParameters().put("projectId", executionQueue.getDetail().getProjectIds());
LoggerUtil.info("进入并行模式,准备执行场景:[ " +
executeQueue.get(reportId).getReport().getName() + " ]", reportId);
} catch (Exception e) {
remakeReportService.testEnded(runRequest, e.getMessage());
LoggerUtil.error("脚本处理失败", runRequest.getReportId(), e);
continue;
}
runRequest.getExtendedParameters().put("projectId", executionQueue.getDetail().getProjectIds());
LoggerUtil.info("进入并行模式,准备执行场景:[ " + executeQueue.get(reportId).getReport().getName() + " ]", reportId);
jMeterService.run(runRequest);
}
}

View File

@ -21,7 +21,9 @@ import io.metersphere.commons.utils.RequestParamsUtil;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.environment.service.BaseEnvironmentService;
import io.metersphere.service.RemakeReportService;
import io.metersphere.utils.LoggerUtil;
import jakarta.annotation.Resource;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jorphan.collections.HashTree;
@ -30,8 +32,6 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@ -54,6 +54,8 @@ public class ApiScenarioSerialService {
private BaseEnvironmentService apiTestEnvironmentService;
@Resource
private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
@Resource
private RemakeReportService remakeReportService;
public void serial(DBTestQueue executionQueue) {
ApiExecutionQueueDetail queue = executionQueue.getDetail();
@ -63,8 +65,6 @@ public class ApiScenarioSerialService {
}
JmeterRunRequestDTO runRequest = RequestParamsUtil.init(executionQueue, queue, reportId);
runRequest.setRunType(RunModeConstants.SERIAL.toString());
// 更新报告状态
updateReportToRunning(queue, runRequest);
try {
if (StringUtils.isEmpty(executionQueue.getPoolId())) {
if (StringUtils.equalsAny(executionQueue.getRunMode(), ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
@ -99,10 +99,14 @@ public class ApiScenarioSerialService {
}
// 开始执行
runRequest.getExtendedParameters().put("projectId", queue.getProjectIds());
jMeterService.run(runRequest);
} catch (Exception e) {
LoggerUtil.error("串行执行失败", e);
remakeReportService.testEnded(runRequest, e.getMessage());
LoggerUtil.error("脚本处理失败", runRequest.getReportId(), e);
return;
}
// 更新报告状态
updateReportToRunning(queue, runRequest);
jMeterService.run(runRequest);
}
/**

View File

@ -21,10 +21,7 @@ import io.metersphere.dto.*;
import io.metersphere.engine.Engine;
import io.metersphere.jmeter.JMeterBase;
import io.metersphere.jmeter.LocalRunner;
import io.metersphere.service.ApiPoolDebugService;
import io.metersphere.service.PluginService;
import io.metersphere.service.RedisTemplateService;
import io.metersphere.service.RemakeReportService;
import io.metersphere.service.*;
import io.metersphere.utils.LoggerUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
@ -203,8 +200,7 @@ public class JMeterService {
LoggerUtil.info("调用资源池开始执行", request.getReportId());
apiPoolDebugService.run(request, resources);
} catch (Exception e) {
LoggerUtil.error(e);
remakeReportService.testEnded(request, e.getMessage());
remakeReportService.updateReport(request, e.getMessage());
LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 执行失败,进行数据回滚:", request.getReportId(), e);
MSException.throwException("调用资源池执行失败,请检查资源池是否配置正常");
}
@ -221,18 +217,15 @@ public class JMeterService {
config = SmoothWeighted.getResource(request.getPoolId());
}
if (config == null) {
LoggerUtil.info("未获取到资源池,请检查配置【系统设置-系统-测试资源池】", request.getReportId());
remakeReportService.testEnded(request, "未获取到资源池,请检查配置【系统设置-系统-测试资源池】");
return;
String errorMsg = "未获取到资源池,请检查配置【系统设置-系统-测试资源池】";
MSException.throwException(errorMsg);
}
request.setCorePoolSize(config.getCorePoolSize());
request.setEnable(config.isEnable());
LoggerUtil.info("开始发送请求【 " + request.getTestId() + " 】到 " + config.getUrl() + " 节点执行", request.getReportId());
ResponseEntity<String> result = restTemplate.postForEntity(config.getUrl(), request, String.class);
if (result == null || !StringUtils.equals("SUCCESS", result.getBody())) {
remakeReportService.testEnded(request, result.getBody());
LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 到" + config.getUrl() + " 节点执行失败", request.getReportId());
LoggerUtil.info(result.getBody());
}
} catch (Exception e) {
remakeReportService.testEnded(request, e.getMessage());

View File

@ -12,6 +12,7 @@ import io.metersphere.base.domain.TestResourcePool;
import io.metersphere.base.mapper.TestResourcePoolMapper;
import io.metersphere.commons.constants.ElementConstants;
import io.metersphere.commons.constants.ResourcePoolTypeEnum;
import io.metersphere.commons.exception.MSException;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.JmeterRunRequestDTO;
@ -19,7 +20,6 @@ import io.metersphere.dto.ProjectJarConfig;
import io.metersphere.dto.RunModeConfigDTO;
import io.metersphere.environment.service.BaseEnvGroupProjectService;
import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.service.RemakeReportService;
import io.metersphere.utils.LoggerUtil;
import io.metersphere.vo.BooleanPool;
import org.apache.commons.collections.MapUtils;
@ -163,9 +163,9 @@ public class GenerateHashTreeUtil {
LoggerUtil.info("场景资源:" + item.getName() + ", 生成执行脚本JMX成功", runRequest.getReportId());
} catch (Exception ex) {
LoggerUtil.error("场景资源:" + item.getName() + ", 生成执行脚本失败", runRequest.getReportId(), ex);
RemakeReportService remakeReportService = CommonBeanFactory.getBean(RemakeReportService.class);
remakeReportService.testEnded(runRequest, ex.getMessage());
LoggerUtil.error("场景资源:" + item.getName() + ", 生成执行脚本失败",
runRequest.getReportId(), ex);
MSException.throwException("场景资源:" + item.getName() + ", 生成执行脚本失败");
}
LogUtil.info(testPlan.getJmx(jmeterHashTree));

View File

@ -164,7 +164,7 @@ public class ApiJMeterFileService {
return zipFilesToByteArray((reportId + "_" + remoteTestId), reportId, hashTree);
}
} catch (Exception e) {
remakeReportService.testEnded(runRequest, "生成执行脚本异常:" + e.getMessage());
LoggerUtil.error("生成脚本失败", reportId, e);
}
return new byte[0];
}

View File

@ -17,7 +17,7 @@ public class RemakeReportService {
@Resource
private RedisTemplateService redisTemplateService;
public void testEnded(JmeterRunRequestDTO request, String errorMsg) {
public void queueNext(JmeterRunRequestDTO request, String errorMsg) {
try {
ResultDTO dto = new ResultDTO();
BeanUtils.copyBean(dto, request);
@ -26,9 +26,7 @@ public class RemakeReportService {
LoggerUtil.info("进入异常结果处理:" + dto.getRunMode() + " 整体处理完成", dto.getReportId());
// 全局并发队列
PoolExecBlockingQueueUtil.offer(dto.getReportId());
String consoleMsg = FixedCapacityUtil.getJmeterLogger(dto.getReportId(), true);
dto.setConsole(consoleMsg + StringUtils.LF + errorMsg);
LoggerUtil.error("执行异常处理:" + errorMsg, request.getReportId());
if (StringUtils.isNotEmpty(dto.getQueueId())) {
CommonBeanFactory.getBean(ApiExecutionQueueService.class).queueNext(dto);
}
@ -42,4 +40,19 @@ public class RemakeReportService {
redisTemplateService.delete(JmxFileUtil.getExecuteFileKeyInRedis(request.getReportId()));
}
}
public void updateReport(JmeterRunRequestDTO request, String errorMsg) {
ResultDTO dto = new ResultDTO();
BeanUtils.copyBean(dto, request);
dto.setQueueId(request.getQueueId());
dto.setTestId(request.getTestId());
String consoleMsg = FixedCapacityUtil.getJmeterLogger(dto.getReportId(), true);
dto.setConsole(consoleMsg + StringUtils.LF + errorMsg);
CommonBeanFactory.getBean(TestResultService.class).testEnded(dto);
}
public void testEnded(JmeterRunRequestDTO request, String errorMsg) {
updateReport(request, errorMsg);
queueNext(request, errorMsg);
}
}