diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java index 4d24e6cdcc..74d77e404c 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java @@ -6,6 +6,7 @@ import io.metersphere.api.dto.definition.request.assertions.MsAssertions; import io.metersphere.api.dto.definition.request.auth.MsAuthManager; import io.metersphere.api.dto.definition.request.controller.MsIfController; import io.metersphere.api.dto.definition.request.controller.MsLoopController; +import io.metersphere.api.dto.definition.request.controller.MsRetryLoopController; import io.metersphere.api.dto.definition.request.controller.MsTransactionController; import io.metersphere.api.dto.definition.request.dns.MsDNSCacheManager; import io.metersphere.api.dto.definition.request.extract.MsExtract; @@ -1013,4 +1014,17 @@ public class ElementUtil { tree.add(headerManager); } } + + public static HashTree retryHashTree(String name, long retryNum, HashTree tree) { + if (StringUtils.isNotBlank(name) && + (name.startsWith(ResultParseUtil.POST_PROCESS_SCRIPT) || + name.startsWith(ResultParseUtil.PRE_PROCESS_SCRIPT))) { + return tree; + } + MsRetryLoopController loopController = new MsRetryLoopController(); + loopController.setClazzName(MsRetryLoopController.class.getCanonicalName()); + loopController.setRetryNum(retryNum); + loopController.setEnable(true); + return loopController.controller(tree, name); + } } diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java index 5696e2f070..1d5435b342 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/MsScenario.java @@ -107,6 +107,7 @@ public class MsScenario extends MsTestElement { ParameterConfig newConfig = new ParameterConfig(); if (this.isEnvironmentEnable()) { this.setNewConfig(envConfig, newConfig); + newConfig.setRetryNum(config.getRetryNum()); } if (config != null && StringUtils.equals(this.getId(), config.getScenarioId())) { config.setTransferVariables(this.variables); diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java index 559c9f8a42..c240a89e7b 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java @@ -71,7 +71,7 @@ public class ParameterConfig extends MsParameter { */ private boolean isOperating; /** - * 导入/导出操作时取样器的testname值 + * 导入/导出操作时取样器的testName值 */ private String operatingSampleTestName; /** @@ -92,6 +92,10 @@ public class ParameterConfig extends MsParameter { private String browserLanguage; private boolean isApi; + /** + * 失败重试次数 + */ + private long retryNum; /** * 排除生成临界控制器的场景 */ diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsRetryLoopController.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsRetryLoopController.java index 3742de7da7..b38f8d14a0 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsRetryLoopController.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/controller/MsRetryLoopController.java @@ -27,7 +27,7 @@ public class MsRetryLoopController extends MsTestElement { @Override public void toHashTree(HashTree tree, List hashTree, MsParameter msParameter) { - final HashTree groupTree = controller(tree); + final HashTree groupTree = controller(tree, this.getName()); if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { @@ -38,13 +38,13 @@ public class MsRetryLoopController extends MsTestElement { } } - private WhileController initWhileController(String condition) { + private WhileController initWhileController(String condition, String name) { if (StringUtils.isEmpty(condition)) { return null; } WhileController controller = new WhileController(); controller.setEnabled(this.isEnable()); - controller.setName("WhileController"); + controller.setName(StringUtils.join("RetryWhile_", name)); controller.setProperty(TestElement.TEST_CLASS, WhileController.class.getName()); controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("WhileControllerGui")); controller.setCondition(condition); @@ -73,9 +73,9 @@ public class MsRetryLoopController extends MsTestElement { return script; } - private HashTree controller(HashTree tree) { + public HashTree controller(HashTree tree, String name) { String whileCondition = "${__jexl3(" + "\"${" + ms_current_timer + "}\" !=\"stop\")}"; - HashTree hashTree = tree.add(initWhileController(whileCondition)); + HashTree hashTree = tree.add(initWhileController(whileCondition, name)); // 添加超时处理,防止死循环 JSR223Listener postProcessor = new JSR223Listener(); postProcessor.setName("Retry-controller"); diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java index c2884b311a..3c21c31f46 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/MsJSR223Processor.java @@ -58,7 +58,14 @@ public class MsJSR223Processor extends MsTestElement { String resourceId = StringUtils.isNotEmpty(this.getId()) ? this.getId() : this.getResourceId(); ElementUtil.setBaseParams(processor, this.getParent(), config, resourceId, this.getIndex()); - final HashTree jsr223PreTree = tree.add(processor); + // 失败重试 + HashTree jsr223PreTree; + if (config.getRetryNum() > 0) { + final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree); + jsr223PreTree = loopTree.add(processor); + } else { + jsr223PreTree = tree.add(processor); + } if (CollectionUtils.isNotEmpty(hashTree)) { hashTree.forEach(el -> { el.toHashTree(jsr223PreTree, el.getHashTree(), config); diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java index dd78f0d4fd..6024d4f58e 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java @@ -78,9 +78,14 @@ public class MsDubboSampler extends MsTestElement { } hashTree = this.getHashTree(); } - - final HashTree testPlanTree = tree.add(dubboSample(config)); - + // 失败重试 + HashTree testPlanTree; + if (config.getRetryNum() > 0) { + final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree); + testPlanTree = loopTree.add(dubboSample(config)); + } else { + testPlanTree = tree.add(dubboSample(config)); + } //添加全局前后置脚本 EnvironmentConfig envConfig = null; if (config.getConfig() != null) { diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java index fd6df25147..4455688144 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java @@ -167,7 +167,14 @@ public class MsHTTPSamplerProxy extends MsTestElement { } } } - final HashTree httpSamplerTree = tree.add(sampler); + // 失败重试 + HashTree httpSamplerTree; + if (config.getRetryNum() > 0) { + final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree); + httpSamplerTree = loopTree.add(sampler); + } else { + httpSamplerTree = tree.add(sampler); + } // 注意顺序,放在config前面,会优先于环境的请求头生效 if (httpConfig != null && httpConfig.isMock() && StringUtils.isNotEmpty(this.getId())) { //如果选择的是mock环境,则自动添加一个apiHeader。 diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java index ca0249ce3a..c7aacd19ba 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java @@ -114,7 +114,14 @@ public class MsJDBCSampler extends MsTestElement { MSException.throwException(StringUtils.isNotEmpty(this.getName()) ? this.getName() + ":" + message : message); } JDBCSampler jdbcSampler = jdbcSampler(config); - final HashTree samplerHashTree = tree.add(jdbcSampler); + // 失败重试 + HashTree samplerHashTree; + if (config.getRetryNum() > 0) { + final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree); + samplerHashTree = loopTree.add(jdbcSampler); + } else { + samplerHashTree = tree.add(jdbcSampler); + } tree.add(ElementUtil.jdbcDataSource(jdbcSampler.getDataSource(), this.dataSource)); ElementUtil.jdbcArguments(this.getName(), this.getVariables(), tree); diff --git a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java index 3c36ea9127..cb4d750ae2 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java +++ b/api-test/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java @@ -140,7 +140,14 @@ public class MsTCPSampler extends MsTestElement { final HashTree samplerHashTree = new ListedHashTree(); samplerHashTree.add(tcpConfig()); TCPSampler tcpSampler = tcpSampler(config); - tree.set(tcpSampler, samplerHashTree); + // 失败重试 + if (config.getRetryNum() > 0) { + final HashTree loopTree = ElementUtil.retryHashTree(this.getName(), config.getRetryNum(), tree); + loopTree.set(tcpSampler, samplerHashTree); + } else { + tree.set(tcpSampler, samplerHashTree); + } + setUserParameters(samplerHashTree); if (tcpPreProcessor != null && StringUtils.isNotBlank(tcpPreProcessor.getScript())) { samplerHashTree.add(tcpPreProcessor.getShellProcessor()); diff --git a/api-test/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseSerialService.java b/api-test/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseSerialService.java index 1c6ad58ae6..fab89919d1 100644 --- a/api-test/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseSerialService.java +++ b/api-test/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseSerialService.java @@ -26,7 +26,6 @@ import io.metersphere.dto.JmeterRunRequestDTO; import io.metersphere.dto.ProjectJarConfig; import io.metersphere.environment.service.BaseEnvironmentService; import io.metersphere.plugin.core.MsTestElement; -import io.metersphere.service.ApiRetryOnFailureService; import io.metersphere.service.RemakeReportService; import io.metersphere.utils.LoggerUtil; import jakarta.annotation.Resource; @@ -54,8 +53,6 @@ public class ApiCaseSerialService { private RedisTemplate redisTemplate; @Resource private TestPlanApiCaseMapper testPlanApiCaseMapper; - @Resource - private ApiRetryOnFailureService apiRetryOnFailureService; public void serial(DBTestQueue executionQueue) { ApiExecutionQueueDetail queue = executionQueue.getDetail(); @@ -148,21 +145,13 @@ public class ApiCaseSerialService { JSONObject element = JSONUtil.parseObject(caseWithBLOBs.getRequest()); ElementUtil.dataFormatting(element); parse(element, testId, envId, caseWithBLOBs.getProjectId()); - String runData = element.toString(); - if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) { - try { - // 失败重试 - String retryData = apiRetryOnFailureService.retry(runData, runRequest.getRetryNum(), true); - if (StringUtils.isNotBlank(retryData)) { - runData = retryData; - } - } catch (Exception e) { - LoggerUtil.error("失败重试脚本生成失败 ", runRequest.getReportId(), e); - } - } - group.getHashTree().add(JSONUtil.parseObject(runData, MsTestElement.class)); + group.getHashTree().add(JSONUtil.parseObject(element.toString(), MsTestElement.class)); testPlan.getHashTree().add(group); - testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig()); + ParameterConfig config = new ParameterConfig(); + if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) { + config.setRetryNum(runRequest.getRetryNum()); + } + testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), config); LoggerUtil.info("用例资源:" + caseWithBLOBs.getName() + ", 生成执行脚本JMX成功", runRequest.getReportId()); return jmeterHashTree; } diff --git a/api-test/backend/src/main/java/io/metersphere/commons/utils/GenerateHashTreeUtil.java b/api-test/backend/src/main/java/io/metersphere/commons/utils/GenerateHashTreeUtil.java index de7f456ef4..185a7b68c0 100644 --- a/api-test/backend/src/main/java/io/metersphere/commons/utils/GenerateHashTreeUtil.java +++ b/api-test/backend/src/main/java/io/metersphere/commons/utils/GenerateHashTreeUtil.java @@ -17,7 +17,6 @@ import io.metersphere.dto.*; import io.metersphere.environment.service.BaseEnvGroupProjectService; import io.metersphere.plugin.core.MsTestElement; import io.metersphere.service.ApiExecutionQueueService; -import io.metersphere.service.ApiRetryOnFailureService; import io.metersphere.service.RemakeReportService; import io.metersphere.utils.LoggerUtil; import io.metersphere.vo.BooleanPool; @@ -144,22 +143,7 @@ public class GenerateHashTreeUtil { Map> jarsMap = NewDriverManager.getJars(projectIds, runRequest.getPool()); testPlan.setProjectJarIds(jarsMap.keySet().stream().toList()); testPlan.setPoolJarsMap(jarsMap); - String data = definition; - // 失败重试 - if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) { - try { - ApiRetryOnFailureService apiRetryOnFailureService = CommonBeanFactory.getBean(ApiRetryOnFailureService.class); - String retryData = apiRetryOnFailureService.retry(data, runRequest.getRetryNum(), false); - if (StringUtils.isNotBlank(retryData)) { - data = retryData; - } - } catch (Exception e) { - LoggerUtil.error("失败重试脚本生成失败 ", runRequest.getReportId(), e); - } - } - - GenerateHashTreeUtil.parse(data, scenario); - + GenerateHashTreeUtil.parse(definition, scenario); group.setEnableCookieShare(scenario.isEnableCookieShare()); LinkedList scenarios = new LinkedList<>(); scenarios.add(scenario); @@ -170,6 +154,9 @@ public class GenerateHashTreeUtil { ParameterConfig config = new ParameterConfig(); config.setScenarioId(item.getId()); config.setReportType(runRequest.getReportType()); + if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) { + config.setRetryNum(runRequest.getRetryNum()); + } testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), config); LoggerUtil.info("场景资源:" + item.getName() + ", 生成执行脚本JMX成功", runRequest.getReportId()); diff --git a/api-test/backend/src/main/java/io/metersphere/service/ApiRetryOnFailureService.java b/api-test/backend/src/main/java/io/metersphere/service/ApiRetryOnFailureService.java deleted file mode 100644 index 19261796a8..0000000000 --- a/api-test/backend/src/main/java/io/metersphere/service/ApiRetryOnFailureService.java +++ /dev/null @@ -1,88 +0,0 @@ -package io.metersphere.service; - -import io.metersphere.api.dto.definition.request.controller.MsRetryLoopController; -import io.metersphere.commons.utils.JSONUtil; -import io.metersphere.commons.utils.LogUtil; -import io.metersphere.plugin.core.MsTestElement; -import org.apache.commons.lang3.StringUtils; -import org.json.JSONArray; -import org.json.JSONObject; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.UUID; - -@Service -public class ApiRetryOnFailureService { - public final static List requests = List.of( - "HTTPSamplerProxy", - "DubboSampler", - "JDBCSampler", - "TCPSampler", - "JSR223Processor"); - - private final static String HASH_TREE_ELEMENT = "hashTree"; - private final static String TYPE = "type"; - private final static String RESOURCE_ID = "resourceId"; - private final static String RETRY = "MsRetry_"; - private final static String LOOP = "LoopController"; - - public String retry(String data, long retryNum, boolean isCase) { - if (StringUtils.isNotEmpty(data)) { - JSONObject element = JSONUtil.parseObject(data); - if (element != null && isCase) { - return formatSampler(element, retryNum).toString(); - } - if (element != null && element.has(HASH_TREE_ELEMENT) && !StringUtils.equalsIgnoreCase(element.optString(TYPE), LOOP)) { - JSONArray hashTree = element.getJSONArray(HASH_TREE_ELEMENT); - setRetry(hashTree, retryNum); - } - return element.toString(); - } - return null; - } - - public MsTestElement retryParse(String data) { - try { - return JSONUtil.parseObject(data, MsRetryLoopController.class); - } catch (Exception e) { - LogUtil.error(e); - } - return null; - } - - public void setRetry(JSONArray hashTree, long retryNum) { - for (int i = 0; i < hashTree.length(); i++) { - JSONObject element = hashTree.getJSONObject(i); - if (StringUtils.equalsIgnoreCase(element.optString(TYPE), LOOP)) { - continue; - } - JSONObject whileObj = formatSampler(element, retryNum); - if (whileObj != null) { - hashTree.put(i, whileObj); - } else if (element.has(HASH_TREE_ELEMENT)) { - JSONArray elementJSONArray = element.getJSONArray(HASH_TREE_ELEMENT); - setRetry(elementJSONArray, retryNum); - } - } - } - - private JSONObject formatSampler(JSONObject element, long retryNum) { - if (element.has(TYPE) && requests.contains(element.optString(TYPE))) { - MsRetryLoopController loopController = new MsRetryLoopController(); - loopController.setClazzName(MsRetryLoopController.class.getCanonicalName()); - loopController.setName(RETRY + element.optString(RESOURCE_ID)); - loopController.setRetryNum(retryNum); - loopController.setEnable(true); - loopController.setResourceId(UUID.randomUUID().toString()); - - JSONObject whileObj = JSONUtil.parseObject(JSONUtil.toJSONString(loopController)); - JSONArray hashTree = new JSONArray(); - hashTree.put(element); - - whileObj.put(HASH_TREE_ELEMENT, hashTree); - return whileObj; - } - return null; - } -}