springboot如何针对单个接口设置超时时间?

比如a接口设置超时时间为5s,b接口设置超时时间为10s
关注者
11
被浏览
87,561

4 个回答

首先我们得明白一件事,接口超时了,只是等待结果响应的线程提前响应给前端。但实际上运行业务逻辑的线程还在跑。

所以我们只需要自定义这个机制,比如利用切面加并发编程中Future,就能达到这个效果。然后再根据配置,让a接口的超时时间为5秒,B接口的超时时间为10秒。就可以了。

案例:

a和b接口

@RestController
@RequestMapping("/foo")
public class FooController {
    @GetMapping("/a")
    public String a() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        return "ok";
    @GetMapping("/b")
    public String b() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        return "ok";
}

切面

@Component
@Aspect
public class ResponseTimeAspect {
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Around("@annotation(getMapping)")
    public Object around(ProceedingJoinPoint joinPoint, GetMapping getMapping) {
        final Future<Object> future = threadPoolTaskExecutor.submit(() -> {
            try {
                return joinPoint.proceed();
            } catch (Throwable throwable) {
                throw new RuntimeException(throwable);
        try {
            HttpServletRequest request =
                    ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            final String uri = request.getRequestURI();
            if("/foo/a".endsWith(uri)){
                // 获取响应,超过1秒抛出超时异常
                return future.get(1, TimeUnit.SECONDS);
            }else if ("/foo/b".endsWith(uri)){
                return future.get(3, TimeUnit.SECONDS);
            return future.get(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return "被中断了";
        } catch (ExecutionException e) {
            return "执行任务发生异常";
        } catch (TimeoutException e) {
            return "超时";
}

以上判断接口的逻辑可改为配置,catch的异常可以改成任意你想要的。

注意点:由于是单开线程进行调用业务代码,如果业务代码中有从线程上下文获取信息是会出问题,比如在Filter中往ThreadLocal中放入了User, 在Servcie想从ThreadLocal获取User,会获取不到。

解决方案:在调用 joinPoint.proceed 前进行线程上下文传递。

当然,如果有更好的方式可以弃用这个方案,不过我看了下tomcat的源码,没找到很好的切入点。