解决接口响应超时

This commit is contained in:
chen-xin-zhi 2025-05-08 19:02:44 +08:00
parent 4abfc570a0
commit 4a6cca7373
9 changed files with 137 additions and 5 deletions

View File

@ -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")

View File

@ -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(); // 超时时间单位毫秒
}

View File

@ -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<Object> task = () -> {
try {
// 执行目标方法
return joinPoint.proceed();
} catch (Throwable e) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, e.getMessage());
}
};
Future<Object> future = executorService.submit(task); // 提交任务
try {
// 在规定时间内等待任务完成
return future.get(timeoutMillis, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
// 超时后抛出自定义异常
throw new BusinessException(ErrorCode.TIMEOUT_ERROR, "请求超时,超过最大允许时间 " + timeoutMillis + " 毫秒");
}
}
}

View File

@ -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, "请求超时");
/**
* 状态码

View File

@ -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() // 拒绝策略调用者运行
);
}
}

View File

@ -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 "任务完成";
}
}

View File

@ -246,6 +246,9 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
redisTemplate.opsForValue().set(SystemConstant.VERIFICATION_CODE + ":" + verificationCode, verificationCode, 5, TimeUnit.MINUTES);
return verificationCode;
}
}

View File

@ -30,6 +30,12 @@ spring:
springdoc:
default-flat-param-object: true
#线程池配置
threadpool:
corePoolSize: 10
maxPoolSize: 50
queueCapacity: 1024
keepAliveTime: 60
server:

View File

@ -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;