You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot异步控制器仍阻塞:使用CompletableFuture与@Async后仍等待

Spring Boot异步控制器请求超时问题分析与解决

问题场景

开发Spring Boot应用时,尝试通过CompletableFuture和@Async注解实现控制器异步处理。测试时在服务方法中加入Thread.sleep()模拟耗时操作,但控制器仍会等待并最终抛出AsyncRequestTimeoutException,已确认控制器与服务运行在不同线程,需排查原因并解决。

相关代码

控制器代码

@PostMapping
public CompletableFuture<User> createUser(@RequestBody User user) {
    System.out.println(Thread.currentThread().getId()+"-----------------------------------"+System.currentTimeMillis()+"----------"+"inside controller");
    return userService.createUser(user);
}

服务层代码

@Override
@Async("asyncExecutor")
public CompletableFuture<User> createUser(User user) {
    System.out.println(Thread.currentThread().getId()+"-----------------------------------"+System.currentTimeMillis()+"-------------inside services");
    return CompletableFuture.supplyAsync(() ->{
        try {
            Thread.sleep(1000000000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getId());
       return userRepository.save(user);
    });
}

控制台输出

21-----------------------------------1683404458540----------inside controller
34-----------------------------------1683404458543-------------inside services
2023-05-07 01:51:28.812  WARN 36119 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.context.request.async.AsyncRequestTimeoutException]

问题原因分析

  • 双重异步嵌套+超长时间任务:服务层方法已通过@Async标记为异步执行,内部又调用CompletableFuture.supplyAsync()做了二次异步嵌套。Spring MVC的异步请求会等待返回的CompletableFuture完成,但你设置的Thread.sleep(1000000000)(约277小时)远超Spring默认的异步请求超时时间(通常30秒),必然触发超时异常。
  • 默认超时阈值限制:Spring Boot异步请求默认超时时间为30秒,只要异步任务执行时间超过这个值,就会抛出AsyncRequestTimeoutException。

解决建议

1. 移除不必要的异步嵌套

服务层的@Async已经能实现方法异步执行,无需再嵌套CompletableFuture.supplyAsync(),直接同步执行逻辑后返回已完成的CompletableFuture即可:

@Override
@Async("asyncExecutor")
public CompletableFuture<User> createUser(User user) {
    System.out.println(Thread.currentThread().getId()+"-----------------------------------"+System.currentTimeMillis()+"-------------inside services");
    try {
        Thread.sleep(1000000000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.out.println(Thread.currentThread().getId());
    return CompletableFuture.completedFuture(userRepository.save(user));
}

2. 调整异步请求超时时间

如果确实需要长时间运行的异步任务,可在配置文件中修改超时阈值:

# 设置超时时间为3600秒(1小时)
spring.mvc.async.request-timeout=3600000

或通过Java配置类自定义:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        // 设置超时时间为3600秒
        configurer.setDefaultTimeout(3600000);
    }
}

3. 替换超长时间阻塞的测试场景

Thread.sleep(1000000000)是极端测试用例,实际业务中应避免此类长时间同步阻塞。若需处理耗时任务,建议使用消息队列(如RabbitMQ、Kafka)实现任务异步解耦:控制器直接返回任务提交成功的响应,后续通过回调、推送等方式通知用户结果。


内容的提问来源于stack exchange,提问作者Deepak Mathur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 00:47:35