From 4a6cca7373127a68cbdbbc36873aaa60852f2d7e Mon Sep 17 00:00:00 2001 From: chen-xin-zhi <3588068430@qq.com> Date: Thu, 8 May 2025 19:02:44 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=8E=A5=E5=8F=A3=E5=93=8D?= =?UTF-8?q?=E5=BA=94=E8=B6=85=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../promotion/GreenOrangeApplication.java | 2 + .../promotion/annotation/Timeout.java | 12 +++++ .../promotion/aop/TimeoutAspect.java | 54 +++++++++++++++++++ .../promotion/common/ErrorCode.java | 3 +- .../promotion/config/ThreadPoolConfig.java | 44 +++++++++++++++ .../controller/user/UserInfoController.java | 17 ++++-- .../user/impl/UserInfoServiceImpl.java | 3 ++ src/main/resources/application.yml | 6 +++ .../promotion/timeout/MyController.java | 1 + 9 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/greenorange/promotion/annotation/Timeout.java create mode 100644 src/main/java/com/greenorange/promotion/aop/TimeoutAspect.java create mode 100644 src/main/java/com/greenorange/promotion/config/ThreadPoolConfig.java diff --git a/src/main/java/com/greenorange/promotion/GreenOrangeApplication.java b/src/main/java/com/greenorange/promotion/GreenOrangeApplication.java index 955075f..b6c786e 100644 --- a/src/main/java/com/greenorange/promotion/GreenOrangeApplication.java +++ b/src/main/java/com/greenorange/promotion/GreenOrangeApplication.java @@ -3,6 +3,8 @@ package com.greenorange.promotion; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @MapperScan("com.greenorange.promotion.mapper") diff --git a/src/main/java/com/greenorange/promotion/annotation/Timeout.java b/src/main/java/com/greenorange/promotion/annotation/Timeout.java new file mode 100644 index 0000000..d2186ce --- /dev/null +++ b/src/main/java/com/greenorange/promotion/annotation/Timeout.java @@ -0,0 +1,12 @@ +package com.greenorange.promotion.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) // 只用于方法 +@Retention(RetentionPolicy.RUNTIME) // 运行时可用 +public @interface Timeout { + long value(); // 超时时间,单位:毫秒 +} diff --git a/src/main/java/com/greenorange/promotion/aop/TimeoutAspect.java b/src/main/java/com/greenorange/promotion/aop/TimeoutAspect.java new file mode 100644 index 0000000..f57f286 --- /dev/null +++ b/src/main/java/com/greenorange/promotion/aop/TimeoutAspect.java @@ -0,0 +1,54 @@ +package com.greenorange.promotion.aop; + +import com.greenorange.promotion.annotation.Timeout; +import com.greenorange.promotion.common.ErrorCode; +import com.greenorange.promotion.exception.BusinessException; +import com.greenorange.promotion.exception.ThrowUtils; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.*; + +@Aspect +@Component +public class TimeoutAspect { + + + private final ExecutorService executorService; + + // 构造器注入线程池 + @Autowired + public TimeoutAspect(ExecutorService executorService) { + this.executorService = executorService; + } + + @Around("@annotation(timeout)") + public Object handleTimeout(ProceedingJoinPoint joinPoint, Timeout timeout) throws Throwable { + long timeoutMillis = timeout.value(); // 获取超时时间 + + // 创建一个线程池来执行任务 + Callable task = () -> { + try { + // 执行目标方法 + return joinPoint.proceed(); + } catch (Throwable e) { + throw new BusinessException(ErrorCode.OPERATION_ERROR, e.getMessage()); + } + }; + + Future future = executorService.submit(task); // 提交任务 + + try { + // 在规定时间内等待任务完成 + return future.get(timeoutMillis, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + // 超时后抛出自定义异常 + throw new BusinessException(ErrorCode.TIMEOUT_ERROR, "请求超时,超过最大允许时间 " + timeoutMillis + " 毫秒"); + } + } +} diff --git a/src/main/java/com/greenorange/promotion/common/ErrorCode.java b/src/main/java/com/greenorange/promotion/common/ErrorCode.java index 176a79e..51f5d86 100644 --- a/src/main/java/com/greenorange/promotion/common/ErrorCode.java +++ b/src/main/java/com/greenorange/promotion/common/ErrorCode.java @@ -14,7 +14,8 @@ public enum ErrorCode { SYSTEM_ERROR(50000,"系统内部异常"), OPERATION_ERROR(50001,"操作失败"), DATABASE_ERROR(50002, "数据库内部异常"), - LOGIN_ERROR(40110,"登陆状态变更"); + LOGIN_ERROR(40110,"登陆状态变更"), + TIMEOUT_ERROR(40800, "请求超时"); /** * 状态码 diff --git a/src/main/java/com/greenorange/promotion/config/ThreadPoolConfig.java b/src/main/java/com/greenorange/promotion/config/ThreadPoolConfig.java new file mode 100644 index 0000000..0aaf3af --- /dev/null +++ b/src/main/java/com/greenorange/promotion/config/ThreadPoolConfig.java @@ -0,0 +1,44 @@ +package com.greenorange.promotion.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + + +@Data +@Configuration +@ConfigurationProperties(prefix = "threadpool") +public class ThreadPoolConfig { + + // 从配置文件中读取线程池的配置参数 + private int corePoolSize; // 核心线程数 + + private int maxPoolSize; // 最大线程数 + + private int queueCapacity; // 队列容量 + + private long keepAliveTime; // 线程空闲时间,单位秒 + + /** + * 配置线程池 + * @return 线程池实例 + */ + @Bean + public ExecutorService threadPoolExecutor() { + return new ThreadPoolExecutor( + corePoolSize, // 核心线程数 + maxPoolSize, // 最大线程数 + keepAliveTime, // 空闲线程存活时间 + TimeUnit.SECONDS, // 空闲时间单位 + new LinkedBlockingQueue<>(queueCapacity), // 任务队列,设置队列大小 + new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:调用者运行 + ); + } +} diff --git a/src/main/java/com/greenorange/promotion/controller/user/UserInfoController.java b/src/main/java/com/greenorange/promotion/controller/user/UserInfoController.java index 9b22238..31166bb 100644 --- a/src/main/java/com/greenorange/promotion/controller/user/UserInfoController.java +++ b/src/main/java/com/greenorange/promotion/controller/user/UserInfoController.java @@ -6,10 +6,10 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.greenorange.promotion.annotation.RequiresPermission; import com.greenorange.promotion.annotation.SysLog; +import com.greenorange.promotion.annotation.Timeout; import com.greenorange.promotion.common.BaseResponse; import com.greenorange.promotion.common.ErrorCode; import com.greenorange.promotion.common.ResultUtils; -import com.greenorange.promotion.constant.SystemConstant; import com.greenorange.promotion.constant.UserConstant; import com.greenorange.promotion.exception.ThrowUtils; import com.greenorange.promotion.model.dto.CommonBatchRequest; @@ -24,14 +24,11 @@ import com.greenorange.promotion.service.common.CommonService; import com.greenorange.promotion.service.user.UserInfoService; import com.greenorange.promotion.service.user.UserMainInfoService; import com.greenorange.promotion.utils.JWTUtils; -import com.greenorange.promotion.utils.RegexUtils; -import com.greenorange.promotion.utils.SendSmsUtil; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; @@ -408,4 +405,16 @@ public class UserInfoController { } + + + @PostMapping("/test") + @Timeout(5000) // 设置超时时间为 3000 毫秒(3秒) + public String testMethod() throws InterruptedException { + // 模拟长时间的任务,超过 3 秒会抛出超时异常 + Thread.sleep(10000); // 模拟 5 秒的耗时操作 + return "任务完成"; + } + + + } diff --git a/src/main/java/com/greenorange/promotion/service/user/impl/UserInfoServiceImpl.java b/src/main/java/com/greenorange/promotion/service/user/impl/UserInfoServiceImpl.java index 0e29845..300c3eb 100644 --- a/src/main/java/com/greenorange/promotion/service/user/impl/UserInfoServiceImpl.java +++ b/src/main/java/com/greenorange/promotion/service/user/impl/UserInfoServiceImpl.java @@ -246,6 +246,9 @@ public class UserInfoServiceImpl extends ServiceImpl redisTemplate.opsForValue().set(SystemConstant.VERIFICATION_CODE + ":" + verificationCode, verificationCode, 5, TimeUnit.MINUTES); return verificationCode; } + + + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 376b968..4467fa2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -30,6 +30,12 @@ spring: springdoc: default-flat-param-object: true +#线程池配置 +threadpool: + corePoolSize: 10 + maxPoolSize: 50 + queueCapacity: 1024 + keepAliveTime: 60 server: diff --git a/src/test/java/com/greenorange/promotion/timeout/MyController.java b/src/test/java/com/greenorange/promotion/timeout/MyController.java index 235dd4c..6882a37 100644 --- a/src/test/java/com/greenorange/promotion/timeout/MyController.java +++ b/src/test/java/com/greenorange/promotion/timeout/MyController.java @@ -1,5 +1,6 @@ package com.greenorange.promotion.timeout; +import org.junit.jupiter.api.Timeout; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;