Spring Boot基于Callable的异步Controller未生效问题排查
问题分析与解决方案
你的代码本身是正确的,出现串行处理的原因是浏览器对同一URL的GET请求存在并发限制,而非Spring MVC的异步配置问题。
核心原因
主流浏览器(如Chrome)会对同一域名下的相同URL的GET请求进行排队处理,不会同时发起,而是等待第一个请求完成后再发送第二个请求,目的是避免重复请求和利用缓存。这就导致你在两个标签页间隔1秒发起请求时,第二个请求实际上是在第一个请求完成后才被浏览器发送,自然呈现串行效果。
解决方案
1. 修正测试方式
用以下方式测试,就能看到预期的异步效果:
- 命令行并发请求:使用
curl同时发起两个请求curl http://localhost:8080/testCallable & curl http://localhost:8080/testCallable - 添加唯一参数:给每个请求添加不同的查询参数,让浏览器认为是不同请求,比如:
- 第一个请求:
http://localhost:8080/testCallable?t=1 - 第二个请求:
http://localhost:8080/testCallable?t=2
- 第一个请求:
- 专业测试工具:使用Postman、JMeter等工具发起并发请求,这类工具不会有浏览器的请求限制。
2. 优化代码(可选)
你的AsyncConfig中@EnableAsync注解是多余的——Callable类型的Controller异步处理是Spring MVC原生支持的机制,不需要依赖@EnableAsync(该注解是给@Async方法用的),可以移除它,不影响功能:
package com.howtodoinjava.springasyncexample.config; @Configuration public class AsyncConfig implements WebMvcConfigurer { @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { configurer.setTaskExecutor(mvcTaskExecutor()); configurer.setDefaultTimeout(30_000); } @Bean public ThreadPoolTaskExecutor mvcTaskExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setCorePoolSize(10); threadPoolTaskExecutor.setThreadNamePrefix("mvc-task-"); return threadPoolTaskExecutor; } }
验证效果
用正确的测试方式发起请求后,日志会符合预期:
2024-xx-xx xx:xx:xx INFO --- [nio-8080-exec-1] c.h.s.w.c.HelloWorldCallableController : controller hit 2024-xx-xx xx:xx:xx INFO --- [nio-8080-exec-2] c.h.s.w.c.HelloWorldCallableController : controller hit 2024-xx-xx xx:xx:xx INFO --- [ mvc-task-1] c.h.s.w.c.HelloWorldCallableController : finished long job 2024-xx-xx xx:xx:xx INFO --- [ mvc-task-2] c.h.s.w.c.HelloWorldCallableController : finished long job
此时Servlet容器的线程(nio-8080-exec-*)会快速处理完Controller方法并释放,业务逻辑则在你配置的mvc-task-*线程池中异步执行,实现了接口的异步处理。
内容的提问来源于stack exchange,提问作者Genku
相关产品推荐
相关产品推荐

