9D5.异步及异常处理
原创实战手册异步大约 2 分钟
9D5.异步及异常处理
实践中,我们主张同步优先,能同步的尽量同步。使用异步处理时,遵循以下实践。
- 异步方法 - 命名和签名
- 异常处理 - 不应该吞掉异常
- 线程池 - 线程池分配
9D5.1.Async注解
使用@Async
注解的方法,会在taskExecutor
线程池中执行,默认前缀为exec-
,
- 方法名,建议使用
Async
后缀,如orderAsync
- 返回值,使用
Future
,如CompletableFuture
如下代码,不建议使用void
方法,而以Future<Void>
和.complete(null)
替代, 他们的主要区别在于未捕获异常的处理,void
会吃掉异常,不能传递给调用者。
@Async
public void badAsync() {
// exception handled by AsyncConfigurer#getAsyncUncaughtExceptionHandler
}
@Async
public CompletableFuture<Void> goodAsync() {
// exception handled by caller via AOP/ExceptionHandler
return CompletableFuture.completedFuture(null);
}
9D5.2.异步请求
SpringMvc的RequestMapping方法不可使用@Async
,通过返回值完成异步处理。
Future
- 组合使用异步ServiceCallable
- 组合同步service,使用applicationTaskExecutor
线程池,默认前缀app-exec-
DeferredResult
- 相当于Context传入,不建议使用
在SpringBoot3.2中,线程池和异常的处理过程如下,其中,
- request和response分别使用mvc的线程池
- service方法根据调用方式,使用
app-exec-
或exec-
- UncaughtException仅
void
时,使用AsyncUncaughtExceptionHandler
- FailedFuture或非
void
UncaughtException,使用ExceptionHandler
日志输出,大概如下,详见 AsyncControllerTest
## `Future` runs in 3 threads
XNIO-1 o.s.web.servlet.DispatcherServlet
XNIO-1 s.w.s.m.m.a.RequestMappingHandlerMapping
XNIO-1 p.f.w.s.a.c.TestAsyncController
exec-2 p.f.w.s.app.service.TestAsyncService
XNIO-1 o.s.w.c.request.async.WebAsyncManager : Async result set
XNIO-1 o.s.web.servlet.DispatcherServlet : Exiting but response remains open
## UncaughtException in exec thread-pool
exec-2 o.s.w.c.request.async.WebAsyncManager : Async error, dispatch
## FailedFuture in exec thread-pool
exec-2 o.s.w.c.request.async.WebAsyncManager : Async error, dispatch
XNIO-3 o.s.web.servlet.DispatcherServlet : "ASYNC" dispatch
XNIO-3 s.w.s.m.m.a.RequestMappingHandlerAdapter : Resume with async result
## `Callable` runs in 3 threads, with `applicationTaskExecutor`
XNIO-1 o.s.web.servlet.DispatcherServlet
XNIO-1 s.w.s.m.m.a.RequestMappingHandlerMapping
app-exec-2 p.f.w.s.app.service.TestAsyncService
app-exec-2 o.s.w.c.request.async.WebAsyncManager : Async result set
## sync Exception in exec thread-pool
app-exec-2 o.s.w.c.request.async.WebAsyncManager : Async error, dispatch
XNIO-3 o.s.web.servlet.DispatcherServlet : "ASYNC" dispatch
XNIO-3 s.w.s.m.m.a.RequestMappingHandlerAdapter : Resume with async result
## `DeferredResult` runs in 3 threads
XNIO-1 o.s.web.servlet.DispatcherServlet
XNIO-1 s.w.s.m.m.a.RequestMappingHandlerMapping
XNIO-1 p.f.w.s.a.c.TestAsyncController
exec-2 p.f.w.s.app.service.TestAsyncService
exec-2 o.s.w.c.request.async.WebAsyncManager : Async result set
XNIO-1 o.s.web.servlet.DispatcherServlet : Exiting but response remains open
## UncaughtException in web thread-pool
XNIO-1 o.s.w.c.request.async.WebAsyncManager : Async error, dispatch
## FailedFuture in exec thread-pool
exec-2 o.s.w.c.request.async.WebAsyncManager : Async error, dispatch
XNIO-3 o.s.web.servlet.DispatcherServlet : "ASYNC" dispatch
XNIO-3 s.w.s.m.m.a.RequestMappingHandlerAdapter : Resume with async result