Appearance
异步请求与异步调用的区别
看在前面
- 原文:详解SpringBoot中异步请求和异步调用(脚本之家,SpringBoot 场景)
一句话答案
两者的使用场景不同。异步请求用来解决并发请求对服务器造成的压力,从而提高对请求的吞吐量。异步调用是用来做一些非主线流程且不需要实时计算和响应的任务。比如同步日志到 kafka 中做日志分析。
异步请求是会一直等待 response 的,最终需要把结果返回给客户端。而异步调用往往是立即返回给客户端响应、完成这次请求。至于异步调用的任务后台自己慢慢跑就行,客户端不会关心。
一、异步请求
1.1 是什么
异步请求可以先释放容器分配给请求的线程与相关资源,减轻系统负担。响应被延后,在耗时处理(如长时间运算)完成后再返回给客户端。
核心价值是增加了服务器对客户端请求的吞吐量。生产中并发量大时通常用 nginx 做集群负载或消息队列做请求缓冲,所以异步请求实际使用较少。
1.2 四种实现方式
方式一:Servlet 原生
request.startAsync()获取AsyncContext,可添加AsyncListener监听开始、完成、异常、超时事件回调。setTimeout(20000)设置超时;asyncContext.start(...)提交内部线程执行业务。业务完成时必须调用
asyncContext.complete()通知请求结束——此时主线程早已释放。
方式二:返回 Callable
Controller 方法直接返回
Callable<String>,在call()中做耗时处理后返回结果。继承
WebMvcConfigurerAdapter重写configureAsyncSupport,可设置默认超时时间、自定义线程池(ThreadPoolTaskExecutor)、注册TimeoutCallableProcessingInterceptor处理超时。
方式三:WebAsyncTask
在 Callable 外再包一层
WebAsyncTask<String>,构造时指定超时时间(如 3000 ms)。wat.onTimeout(callable)设置超时回调,返回自定义超时提示。
方式四:DeferredResult
适合较复杂的业务,最主要的特点是可以在两个完全不相干的线程间通信。
可设置超时时间、
onTimeout委托处理超时、onCompletion完成后回调。把业务丢到自定义线程池执行,处理完后调用
result.setResult(...)返回结果。
二、异步调用
2.1 是什么、用在哪
- 异步调用比异步请求更常用。适用于与主线业务无关、无紧密性的任务(如记录日志),另起线程处理,让主线程继续执行其他业务。库内另有一篇《Spring异步调用@Async》可对照。
2.2 怎么用
启动类加
@EnableAsync使@Async注解生效。在需异步执行的方法上加
@Async("threadPool"),threadPool 为自定义线程池。
2.3 默认线程池的坑
@Async默认使用SimpleAsyncTaskExecutor,它不是真正的线程池。线程不重用,每次调用都新建线程(从控制台递增的线程名可以看出)。生产上建议自定义线程池。
2.4 @Async 失效的场景
同一个类内调用
@Async方法(含内部类):Spring 创建的是代理对象。同类内部调用走的是自身而非代理,与@Transactional、@Cache失效同理。调用
static方法;调用private方法。
2.5 同类调用失效的两种解法
解法一:通过 ApplicationContext 拿自身代理
- 注入
ApplicationContext,用applicationContext.getBean(EmailController.class)拿到代理对象再调用@Async方法(方法必须是 public 且非 static)。
解法二:暴露 CGLIB 代理,用 AopContext 手动获取
启动类加
@EnableAspectJAutoProxy(exposeProxy = true)。调用处用
(EmailService) AopContext.currentProxy()获取当前代理对象,再调用异步方法。辅助判断工具:
AopUtils.isAopProxy()/isCglibProxy()/isJdkDynamicProxy()。
三、对比与总结
| 维度 | 异步请求 | 异步调用 |
|---|---|---|
| 使用场景 | 解决并发请求对服务器的压力,提高吞吐量 | 非主线流程、不需实时响应的任务(如同步日志到 kafka) |
| 是否等待响应 | 一直等待 response,最终需返回结果给客户端 | 立即返回响应完成请求,任务后台自己跑,客户端不关心 |
| 典型实现 | AsyncContext / Callable / WebAsyncTask / DeferredResult | @EnableAsync + @Async + 自定义线程池 |
- 两者使用场景和机制不同,不要混为一谈。
@Async失效问题的根源在于 Spring 的动态代理机制(AOP)——同类调用绕过了代理对象。
内容来源:原文 · 2026-10 整理回填。