springboot提供了两种异步处理方案,一是传统同步web处理加异步service层,二是异步web处理框架webFlux。
一、传统同步web处理加异步service层
使用流程:在springboot application启动类上添加@EnableAsync注解。在service层类或方法上添加@Async注解,同时在异步方法所在的service层类上添加@Component或@service 等注解,该方法必须返回AsyncResult或CompletableFuture、ListenableFuture、Future之一,然后controller层通过@Autowired自动注入service类并使用异步类。
controller和service层代码如下:
- @RestController
- public class UserCtroller {
-
- @Autowired
- UserService userService;
-
- @RequestMapping( "/n/{name}")
- @ResponseBody
- public User getUserByName(@PathVariable String name){
- AsyncResult
> user = userService.getUserByName( name ); - try{
- return user.get().isEmpty() ? null: user.get().get() ;
- } catch (ExecutionException e) {
- return null;
- }
- }
-
- @RequestMapping( "/l")
- @ResponseBody
- public List
getAll(){ - return userService.getAll();
- }
-
- }
- @Service
- public class UserServiceImpl implements UserService {
-
- List
users = Arrays.asList( - new User( "a", 20 , "aaaa"),
- new User( "b", 20 , "aaaa"),
- new User( "c", 20 , "aaaa")
- );
-
- @Override
- @Async
- public AsyncResult
> getUserByName(String name){ - Optional
user = users.stream().filter( (u) -> Objects.equals( u.getName(), name ) ).findFirst(); - return new AsyncResult<>( user );
- }
-
- @Override
- @Async
- public List
getAll(){ - return users;
- }
- }
在service的 getUserByName方法内置断点,执行监控调用堆栈,结果如下。可以看出spring采用AOP的方式将@Async的注解的方法封装为异步执行。
- getUserByName:26, UserServiceImpl (com.alun.serviceasync)
- invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
- invoke:77, NativeMethodAccessorImpl (jdk.internal.reflect)
- invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
- invoke:568, Method (java.lang.reflect)
- invokeJoinpointUsingReflection:344, AopUtils (org.springframework.aop.support)
- invokeJoinpoint:198, ReflectiveMethodInvocation (org.springframework.aop.framework)
- proceed:163, ReflectiveMethodInvocation (org.springframework.aop.framework)
- lambda$invoke$0:115, AsyncExecutionInterceptor (org.springframework.aop.interceptor)
- call:-1, AsyncExecutionInterceptor$$Lambda$778/0x0000000800fb3710 (org.springframework.aop.interceptor)
- run$$$capture:264, FutureTask (java.util.concurrent)
- run:-1, FutureTask (java.util.concurrent)
- - 异步堆栈跟踪
:132, FutureTask (java.util.concurrent) :44, ListenableFutureTask (org.springframework.util.concurrent) - submitListenable:412, ThreadPoolTaskExecutor (org.springframework.scheduling.concurrent)
- doSubmit:286, AsyncExecutionAspectSupport (org.springframework.aop.interceptor)
- invoke:129, AsyncExecutionInterceptor (org.springframework.aop.interceptor)
- proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)
- invoke:215, JdkDynamicAopProxy (org.springframework.aop.framework)
- getUserByName:-1, $Proxy50 (jdk.proxy2)
- getUserByName:27, UserCtroller (com.alun.serviceasync)
使用数据库事务时一定要注意:在Async方法上标注@Transactional,事务无效。 在Async方法调用的方法上标注@Transactional 有效。
Springboot中使用@Async实现异步处理及注意事项 这篇文章总结的不错,值得大家参考。
二、异步web处理框架webFlux
spring5提供了webFlux异步处理框架,提高系统吞吐量和伸缩性,以 Reactor为核心,基于Servlet3.1规范实现,即从servlet线程就以异步方式处理请求。springboot提供了TomcatReactiveWebServerFactory、NettyReactiveWebServerFactory、UndertowReactiveWebServerFactory、JettyReactiveWebServerFactory四种ReactiveWebServer实现。默认使用容器是 Netty,Netty 是高性能的 NIO 框架。
controller层的方法需要返回Mono或Flux,service代码不需要改动。如:
- @RestController
- public class UserCtroller {
-
- @Autowired
- UserService userService;
-
- @RequestMapping( "/n/{name}")
- public Mono
getUserByName( @PathVariable String name){ - return userService.getUserByName( name );
- }
-
- @RequestMapping( "/l")
- public Flux
getAll(){ - return userService.getAll();
- }
- }
SpringWebflux 提供两种编程模式:注解编程模型和函数式编程模型。
函数式编程模型参见Spring Webflux
两种方式的gradle文件的配置如下,如需切换server提供方式,可以如下修改:
web mvc方式配置:
- // web
- implementation ('org.springframework.boot:spring-boot-starter-web'){
- exclude group:'org.springframework.boot', module:'spring-boot-starter-tomcat'
- }
- implementation 'org.springframework.boot:spring-boot-starter-undertow'
webFlux方式的配置:
- // webflux
- implementation ('org.springframework.boot:spring-boot-starter-webflux'){
- exclude group:'org.springframework.boot', module:'spring-boot-starter-netty'
- }
- implementation 'org.springframework.boot:spring-boot-starter-undertow'