From d9c567c6c398c141e21fbd49964c4450e00ea24c Mon Sep 17 00:00:00 2001 From: luogw <3132758203@qq.com> Date: Tue, 10 Mar 2026 11:11:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A4=E7=89=8C=E6=A1=B6=E9=99=90=E6=B5=81-?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=AE=97=E6=B3=95=E7=94=9F=E9=A2=98=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 8 +++ ...stToGenerateQuestionDomainServiceImpl.java | 24 +++++++-- .../project/interaction/utils/NotifyUtil.java | 53 +++++++++++++++++++ .../impl/CheckMilvusDomainServiceImpl.java | 8 +-- .../config/QuestionGenerationConfig.java | 27 ++++++++++ .../impl/QuestionGenerationRateLimiter.java | 36 +++++++++++++ src/main/resources/application-dev.yml | 8 ++- src/main/resources/application.yml | 8 ++- 8 files changed, 159 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/project/interaction/utils/NotifyUtil.java create mode 100644 src/main/java/com/project/question/config/QuestionGenerationConfig.java create mode 100644 src/main/java/com/project/question/domain/service/impl/QuestionGenerationRateLimiter.java diff --git a/pom.xml b/pom.xml index bda0e0b..220aab2 100644 --- a/pom.xml +++ b/pom.xml @@ -140,6 +140,14 @@ hutool-all 5.8.3 + + + + com.google.guava + guava + 31.1-jre + + org.springframework.boot diff --git a/src/main/java/com/project/interaction/domain/service/impl/PostToGenerateQuestionDomainServiceImpl.java b/src/main/java/com/project/interaction/domain/service/impl/PostToGenerateQuestionDomainServiceImpl.java index 52f641b..657e995 100644 --- a/src/main/java/com/project/interaction/domain/service/impl/PostToGenerateQuestionDomainServiceImpl.java +++ b/src/main/java/com/project/interaction/domain/service/impl/PostToGenerateQuestionDomainServiceImpl.java @@ -1,12 +1,13 @@ package com.project.interaction.domain.service.impl; import cn.hutool.core.util.RandomUtil; -import com.project.information.domain.service.KnowledgePointBaseService; import com.project.interaction.domain.dto.GenerateQuestionRequestDTO; import com.project.interaction.domain.dto.GenerateQuestionResponseDTO; import com.project.interaction.domain.service.PostToGenerateQuestionDomainService; +import com.project.interaction.utils.NotifyUtil; import com.project.question.domain.dto.QuestionDTO; import com.project.question.domain.dto.TaskKnowledgePointDTO; +import com.project.question.domain.service.impl.QuestionGenerationRateLimiter; import com.project.question.domain.service.SaveQuestionDomainService; import com.project.task.domain.enums.QuestionTypeEnum; import jakarta.annotation.Resource; @@ -37,6 +38,12 @@ public class PostToGenerateQuestionDomainServiceImpl implements PostToGenerateQu @Autowired private SaveQuestionDomainService saveQuestionDomainService; + @Autowired + private QuestionGenerationRateLimiter rateLimiter; + + @Autowired + private NotifyUtil notifyUtil; + private final String[] optionListStr = {"A", "B", "C", "D"}; private final String[] trueFalseListStr = {"A", "B"}; @@ -59,13 +66,20 @@ public class PostToGenerateQuestionDomainServiceImpl implements PostToGenerateQu taskId, clusterId, questionType.getDescription(), num); try { + // 应用限流:获取令牌,超过限流速率则等待 + long waitStart = System.currentTimeMillis(); + rateLimiter.acquire(); + long waitTime = System.currentTimeMillis() - waitStart; + if (waitTime > 100) { + log.info(">>> [题目生成] 因限流等待了 {} ms, TaskId: {}", waitTime, taskId); + } + //尝试调用算法服务生成题目 GenerateQuestionResponseDTO response = callAlgorithmService(dtoList, questionType, num); if (response != null && StringUtils.isNotBlank(response.getMessage()) && response.getMessage().contains("success")) { log.info(">>> [题目生成] 算法服务接受任务成功, 任务: {}, 状态: {}, 信息:{}", response.getTaskId(), response.getStatus(),response.getMessage()); - return; } } catch (Exception e) { @@ -74,8 +88,10 @@ public class PostToGenerateQuestionDomainServiceImpl implements PostToGenerateQu } //熔断降级:算法服务失败时使用硬编码生成默认题目 - log.info(">>> [题目生成] 启用熔断降级,生成默认题目"); - generateFallbackQuestions(dtoList, questionType, num); +// log.info(">>> [题目生成] 启用熔断降级,生成默认题目"); +// generateFallbackQuestions(dtoList, questionType, num); + //发送预警通知 + notifyUtil.notify(taskId,clusterId); } /** diff --git a/src/main/java/com/project/interaction/utils/NotifyUtil.java b/src/main/java/com/project/interaction/utils/NotifyUtil.java new file mode 100644 index 0000000..c607de4 --- /dev/null +++ b/src/main/java/com/project/interaction/utils/NotifyUtil.java @@ -0,0 +1,53 @@ +package com.project.interaction.utils; + +import com.github.tingyugetc520.ali.dingtalk.api.DtService; +import com.github.tingyugetc520.ali.dingtalk.bean.message.DtCorpConversationMessage; +import com.github.tingyugetc520.ali.dingtalk.bean.message.DtCorpConversationMsgSendResult; +import com.github.tingyugetc520.ali.dingtalk.bean.message.DtMessage; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; + +@Slf4j +@Component +public class NotifyUtil { + + @Autowired + private DtService dtService; + + public void notify(Long taskId,Long clusterId) { +// "283712335120874575", "01231011386731976125", + ArrayList userIDList = Lists.newArrayList( "283712335120874575", "01231011386731976125","590204300932000212"); + + try { + String markdown = String.format("### 【告警】算法生题API调用失败通知 \n\n") + + String.format("## 核心信息\n") + + String.format("- 考试任务ID:%d\n", taskId) + + String.format("- 知识点簇ID:%d\n\n", clusterId) + + String.format("## 异常说明\n") + + String.format("算法服务生成题目时调用失败,可能导致考试题目不完整或无法正常作答,请尽快排查以下问题:\n") + + String.format("- 算法服务是否正常运行\n"); + DtCorpConversationMessage corpConversationMessage = DtCorpConversationMessage.builder() + .agentId(dtService.getDtConfigStorage().getAgentId()) + .userIds(Lists.newArrayList(userIDList)) + .msg(DtMessage.MARKDOWN() + .content("算法生题API调用失败通知") + .text(markdown) + .build()) + .build(); + DtCorpConversationMsgSendResult result = dtService.getCorpConversationMsgService().send(corpConversationMessage); + if (result.getErrCode() == 0) { + log.info(">>> [算法生题API调用失败通知] 成功发送通知 ,考试任务ID:{} ,知识点簇ID:{}", taskId, clusterId); + } else { + log.error(">>> [算法生题API调用失败通知] 发送失败.考试任务ID:{} ,知识点簇ID:{}, 错误码: {}, 错误信息: {}", taskId, clusterId, + result.getErrCode(), result.getErrMsg()); + } + + } catch (Exception e) { + log.error(">>> [算法生题API调用失败通知] 钉钉接口调用异常, 考试任务ID:{} ,知识点簇ID:{}, 原因: {}",taskId, clusterId, e.getMessage()); + } + } +} diff --git a/src/main/java/com/project/milvus/domain/service/impl/CheckMilvusDomainServiceImpl.java b/src/main/java/com/project/milvus/domain/service/impl/CheckMilvusDomainServiceImpl.java index fc5b090..00be3f1 100644 --- a/src/main/java/com/project/milvus/domain/service/impl/CheckMilvusDomainServiceImpl.java +++ b/src/main/java/com/project/milvus/domain/service/impl/CheckMilvusDomainServiceImpl.java @@ -43,9 +43,8 @@ public class CheckMilvusDomainServiceImpl implements CheckMilvusDomainService { } List pointIdsList = title.getTaskKpIdsList(); - List titleVectorList = title.getTitleVectorList(); - if (CollectionUtil.isEmpty(pointIdsList) || CollectionUtil.isEmpty(titleVectorList)) { + if (CollectionUtil.isEmpty(pointIdsList)) { throw new MissingParameterException("请求参数缺失或格式错误"); } @@ -57,11 +56,6 @@ public class CheckMilvusDomainServiceImpl implements CheckMilvusDomainService { throw new MissingParameterException("考试任务ID不能为空"); } - - if(titleVectorList.size() != 1024){ - throw new MissingParameterException("向量格式错误"); - } - // 校验 QuestionDetailDTO 及其内部参数 QuestionDTO.QuestionDetailDTO detailDTO = title.QuestionDetailDTO; if (detailDTO == null) { diff --git a/src/main/java/com/project/question/config/QuestionGenerationConfig.java b/src/main/java/com/project/question/config/QuestionGenerationConfig.java new file mode 100644 index 0000000..6d79609 --- /dev/null +++ b/src/main/java/com/project/question/config/QuestionGenerationConfig.java @@ -0,0 +1,27 @@ +package com.project.question.config; + +import com.project.question.domain.service.impl.QuestionGenerationRateLimiter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 题目生成相关配置 + */ +@Configuration +public class QuestionGenerationConfig { + + @Value("${question.generation.rate-limit:5}") + private double rateLimit; + + /** + * 创建题目生成限流器 + */ + @Bean + @ConditionalOnMissingBean(QuestionGenerationRateLimiter.class) + public QuestionGenerationRateLimiter questionGenerationRateLimiter() { + return new QuestionGenerationRateLimiter(this.rateLimit); + } +} + diff --git a/src/main/java/com/project/question/domain/service/impl/QuestionGenerationRateLimiter.java b/src/main/java/com/project/question/domain/service/impl/QuestionGenerationRateLimiter.java new file mode 100644 index 0000000..7705e1c --- /dev/null +++ b/src/main/java/com/project/question/domain/service/impl/QuestionGenerationRateLimiter.java @@ -0,0 +1,36 @@ +package com.project.question.domain.service.impl; + +import com.google.common.util.concurrent.RateLimiter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 题目生成API限流器 + * 用于控制调用算法服务的请求速率,防止一大批请求同时涌入导致服务压力过大 + */ +@Component +@Slf4j +public class QuestionGenerationRateLimiter { + + /** + * RateLimiter 使用令牌桶算法 + * 每秒允许固定数量的请求通过 + */ + private final RateLimiter rateLimiter; + + public QuestionGenerationRateLimiter(@Value("${question.generation.rate-limit:5}") double permitsPerSecond) { + this.rateLimiter = RateLimiter.create(permitsPerSecond); + log.info(">>> [限流器初始化] 题目生成限流速率设置为: {}/秒", permitsPerSecond); + } + + /** + * 获取令牌,如果令牌不足则阻塞等待 + * 调用此方法的线程会被阻塞,直到获取到令牌为止 + */ + public void acquire() { + rateLimiter.acquire(); + } + +} + diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index a90680c..6a2ea8c 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -73,4 +73,10 @@ algo: clusterUrl: /api/algorithm/v1/cluster baseUrl: / generateQuestionUrl: /v1/generate/questions_from_cluster - apiUrl: http://8.129.84.155:8000 \ No newline at end of file + apiUrl: http://8.129.84.155:8000 + +# 题目生成相关配置 +question: + generation: + # 限流速率:每秒允许的API请求数 + rate-limit: 20 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 52e5ca4..1d9a178 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -78,4 +78,10 @@ algo: generateQuestionUrl: /v1/generate/questions_from_cluster apiUrl: http://8.129.84.155:8000 jwt: - secret: "my-very-fixed-and-secure-secret-key-1234567890" \ No newline at end of file + secret: "my-very-fixed-and-secure-secret-key-1234567890" + +# 题目生成相关配置 +question: + generation: + # 限流速率:每秒允许的API请求数 + rate-limit: 20