From 23092f013c54978c8ea47a52a25b1ed1bc870092 Mon Sep 17 00:00:00 2001 From: AgAngle <1323481023@qq.com> Date: Wed, 16 Oct 2024 14:01:35 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E4=BB=BB=E5=8A=A1=E5=BC=80=E5=A7=8B=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E6=97=B6=EF=BC=8C=E6=B2=A1=E6=9C=89=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=BC=80=E5=A7=8B=E6=97=B6=E9=97=B4=E5=92=8C=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/constants/CommonConstants.java | 1 + .../sdk/dto/api/task/GetRunScriptRequest.java | 10 +++ .../sdk/dto/api/task/TaskInfo.java | 7 ++ .../service/ApiExecuteResourceService.java | 81 ++++++++++++++----- .../ApiTestCaseBatchRunService.java | 1 + .../scenario/ApiScenarioBatchRunService.java | 1 + .../api/utils/TaskRunningCache.java | 66 +++++++++++++++ .../TestPlanApiCaseBatchRunService.java | 1 + .../TestPlanApiScenarioBatchRunService.java | 1 + 9 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 backend/services/api-test/src/main/java/io/metersphere/api/utils/TaskRunningCache.java diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/CommonConstants.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/CommonConstants.java index 2cfcfce503..a2c716d521 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/CommonConstants.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/CommonConstants.java @@ -6,4 +6,5 @@ package io.metersphere.sdk.constants; */ public class CommonConstants { public static final String DEFAULT_NULL_VALUE = "NONE"; + public static final String RUNNING_TASK_PREFIX = "RUNNING_TASK:"; } diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/GetRunScriptRequest.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/GetRunScriptRequest.java index 5641295bb4..e951e1d511 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/GetRunScriptRequest.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/GetRunScriptRequest.java @@ -48,4 +48,14 @@ public class GetRunScriptRequest implements Serializable { * 线程ID */ private String threadId; + /** + * 是否是批量执行 + * 包括用例的批量执行 + * 测试计划的执行 + */ + private Boolean batch; + /** + * 任务ID + */ + private String taskId; } diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskInfo.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskInfo.java index e756dcc4a3..9c4e3da272 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskInfo.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/dto/api/task/TaskInfo.java @@ -102,4 +102,11 @@ public class TaskInfo implements Serializable { * 记录执行时的环境变量 */ private List environmentVariables; + + /** + * 是否是批量执行 + * 包括用例的批量执行 + * 测试计划的执行 + */ + private Boolean batch = false; } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteResourceService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteResourceService.java index 4d27be3923..8a4b798f28 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteResourceService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiExecuteResourceService.java @@ -3,17 +3,18 @@ package io.metersphere.api.service; import io.metersphere.api.invoker.ApiExecuteCallbackServiceInvoker; import io.metersphere.api.service.definition.ApiReportService; import io.metersphere.api.service.scenario.ApiScenarioReportService; -import io.metersphere.sdk.constants.ApiExecuteResourceType; -import io.metersphere.sdk.constants.ApiExecuteRunMode; -import io.metersphere.sdk.constants.ExecStatus; +import io.metersphere.api.utils.TaskRunningCache; +import io.metersphere.sdk.constants.*; import io.metersphere.sdk.dto.api.task.GetRunScriptRequest; import io.metersphere.sdk.dto.api.task.GetRunScriptResult; import io.metersphere.sdk.dto.api.task.TaskItem; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.EnumValidator; import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.domain.ExecTask; import io.metersphere.system.domain.ExecTaskItem; import io.metersphere.system.mapper.ExecTaskItemMapper; +import io.metersphere.system.mapper.ExecTaskMapper; import jakarta.annotation.Resource; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; @@ -27,6 +28,8 @@ import java.util.Optional; @Transactional(rollbackFor = Exception.class) public class ApiExecuteResourceService { + @Resource + private ExecTaskMapper execTaskMapper; @Resource private ExecTaskItemMapper execTaskItemMapper; @Resource @@ -35,33 +38,17 @@ public class ApiExecuteResourceService { private ApiScenarioReportService apiScenarioReportService; @Resource private StringRedisTemplate stringRedisTemplate; + @Resource + private TaskRunningCache taskRunningCache; public GetRunScriptResult getRunScript(GetRunScriptRequest request) { TaskItem taskItem = request.getTaskItem(); String taskItemId = taskItem.getId(); - String reportId = taskItem.getReportId(); LogUtils.info("生成并获取执行脚本: {}", taskItem.getId()); - ApiExecuteResourceType apiExecuteResourceType = EnumValidator.validateEnum(ApiExecuteResourceType.class, request.getResourceType()); - if (!ApiExecuteRunMode.isDebug(request.getRunMode())) { - // 更新任务项状态 - ExecTaskItem execTaskItem = new ExecTaskItem(); - execTaskItem.setId(taskItem.getId()); - execTaskItem.setStartTime(System.currentTimeMillis()); - execTaskItem.setStatus(ExecStatus.RUNNING.name()); - execTaskItem.setThreadId(request.getThreadId()); - execTaskItemMapper.updateByPrimaryKeySelective(execTaskItem); - - // 非调试执行,更新报告状态 - switch (apiExecuteResourceType) { - case API_SCENARIO, TEST_PLAN_API_SCENARIO, PLAN_RUN_API_SCENARIO -> - apiScenarioReportService.updateReportRunningStatus(reportId); - case API_CASE, TEST_PLAN_API_CASE, PLAN_RUN_API_CASE -> - apiReportService.updateReportRunningStatus(reportId); - default -> throw new MSException("不支持的资源类型: " + request.getResourceType()); - } + updateRunningReportStatus(request); } if (BooleanUtils.isFalse(request.getNeedParseScript())) { @@ -75,4 +62,54 @@ public class ApiExecuteResourceService { return ApiExecuteCallbackServiceInvoker.getRunScript(request.getResourceType(), request); } + + private void updateRunningReportStatus(GetRunScriptRequest request) { + TaskItem taskItem = request.getTaskItem(); + String taskId = request.getTaskId(); + + String reportId = taskItem.getReportId(); + ApiExecuteResourceType apiExecuteResourceType = EnumValidator.validateEnum(ApiExecuteResourceType.class, request.getResourceType()); + + if (request.getBatch()) { + // 设置缓存成功说明是第一个任务,则设置任务的开始时间和运行状态 + if (taskRunningCache.setIfAbsent(taskId)) { + // 将任务状态更新为运行中 + updateTaskRunningStatus(taskId); + } + } else { + // 非批量时,直接更新任务状态 + updateTaskRunningStatus(taskId); + } + + // 更新任务项状态 + updateTaskItemRunningStatus(request); + + // 非调试执行,更新报告状态 + switch (apiExecuteResourceType) { + case API_SCENARIO, TEST_PLAN_API_SCENARIO, PLAN_RUN_API_SCENARIO -> + apiScenarioReportService.updateReportRunningStatus(reportId); + case API_CASE, TEST_PLAN_API_CASE, PLAN_RUN_API_CASE -> + apiReportService.updateReportRunningStatus(reportId); + default -> throw new MSException("不支持的资源类型: " + request.getResourceType()); + } + } + + private void updateTaskRunningStatus(String taskId) { + ExecTask execTask = new ExecTask(); + execTask.setId(taskId); + execTask.setStartTime(System.currentTimeMillis()); + execTask.setStatus(ExecStatus.RUNNING.name()); + execTaskMapper.updateByPrimaryKeySelective(execTask); + } + + private void updateTaskItemRunningStatus(GetRunScriptRequest request) { + TaskItem taskItem = request.getTaskItem(); + // 更新任务项状态 + ExecTaskItem execTaskItem = new ExecTaskItem(); + execTaskItem.setId(taskItem.getId()); + execTaskItem.setStartTime(System.currentTimeMillis()); + execTaskItem.setStatus(ExecStatus.RUNNING.name()); + execTaskItem.setThreadId(request.getThreadId()); + execTaskItemMapper.updateByPrimaryKeySelective(execTaskItem); + } } \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java index f2d5055bd3..f2621c6102 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java @@ -435,6 +435,7 @@ public class ApiTestCaseBatchRunService { public TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { TaskInfo taskInfo = apiTestCaseService.getTaskInfo(projectId, ApiExecuteRunMode.RUN.name()); + taskInfo.setBatch(true); return apiBatchRunBaseService.setBatchRunTaskInfoParam(runModeConfig, taskInfo); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java index 88f8b033e2..fef0a159e3 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java @@ -392,6 +392,7 @@ public class ApiScenarioBatchRunService { public TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { TaskInfo taskInfo = apiScenarioRunService.getTaskInfo(projectId, ApiExecuteRunMode.RUN.name()); + taskInfo.setBatch(true); return apiBatchRunBaseService.setBatchRunTaskInfoParam(runModeConfig, taskInfo); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/utils/TaskRunningCache.java b/backend/services/api-test/src/main/java/io/metersphere/api/utils/TaskRunningCache.java new file mode 100644 index 0000000000..738eaaebe6 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/utils/TaskRunningCache.java @@ -0,0 +1,66 @@ +package io.metersphere.api.utils; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import io.metersphere.sdk.constants.CommonConstants; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * 记录正在运行的任务 + * runningTasks 作为内存级别的一级缓存,减少网络交互 + * redis 作为分布式的二级缓存 + * 执行结束后,result-hub 清除二级缓存 + * + * @Author: jianxing + * @CreateTime: 2024-10-09 10:57 + */ +@Component +public class TaskRunningCache { + + @Resource + private StringRedisTemplate stringRedisTemplate; + /** + * 记录正在运行的任务 + */ + private final Cache runningTasks = CacheBuilder.newBuilder() + .expireAfterWrite(30, TimeUnit.SECONDS) + .build(); + + /** + * 如果没有缓存则设置缓存 + * 设置成功,则返回 true,说明没有缓存 + * 设置失败,则返回 false,说明有缓存 + * @param taskId + * @return + */ + public boolean setIfAbsent(String taskId) { + Boolean hasCache = BooleanUtils.isTrue(runningTasks.getIfPresent(taskId)); + if (!hasCache) { + // 原子操作,没有线程安全问题 + // 一级缓存没有,则查询二级缓存 + Boolean success = stringRedisTemplate.opsForValue() + .setIfAbsent( + getKey(taskId), + StringUtils.EMPTY, + 1, + TimeUnit.DAYS + ); + + // 设置二级缓存 + runningTasks.put(taskId, true); + + return success; + } + return false; + } + + private static String getKey(String taskId) { + return CommonConstants.RUNNING_TASK_PREFIX + taskId; + } +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java index bfa7a14ec6..04116df4e8 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java @@ -403,6 +403,7 @@ public class TestPlanApiCaseBatchRunService { private TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { TaskInfo taskInfo = apiTestCaseBatchRunService.getTaskInfo(projectId, runModeConfig); + taskInfo.setBatch(true); taskInfo.setResourceType(ApiExecuteResourceType.TEST_PLAN_API_CASE.name()); return taskInfo; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java index d3a5b779bd..95f9274ce3 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java @@ -391,6 +391,7 @@ public class TestPlanApiScenarioBatchRunService { private TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { TaskInfo taskInfo = apiScenarioBatchRunService.getTaskInfo(projectId, runModeConfig); + taskInfo.setBatch(true); taskInfo.setResourceType(ApiExecuteResourceType.TEST_PLAN_API_SCENARIO.name()); return taskInfo; }