MVC之Callable异步请求处理方式
文章目录
MVC异步请求支持
Servlet 3.0开始支持异步请求的处理,SpringMVC 3.2开始支持Servlet的这种特性.
- 项目中修改web.xml,申明web-app_3_0.xsd
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
- 在Servlet配置中启用异步请求支持,本Demo中使用的Spring版本为4.3.9
配置Spring的线程池,作为MVC的异步支持<servlet> <servlet-name>SpringMVCServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:app_config.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> <!-- 这里开启异步支持 --> <async-supported>true</async-supported> </servlet>
ps: Spring线程池及相关阻塞队列的内容详见:<mvc:async-support task-executor="asyncRunBusinessThreadPoolTaskExecutor" default-timeout="30000"/> <bean id="asyncRunBusinessThreadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <!-- 核心线程数 --> <property name="corePoolSize" value="32"/> <!-- 最大线程数 --> <property name="maxPoolSize" value="1000"/> <!-- 创建阻塞队列,线程池满了直接在调用线程上执行 --> <property name="queueCapacity" value="0"/> <!-- 线程池维护线程所允许的空闲时间 --> <property name="keepAliveSeconds" value="300"/> <property name="threadNamePrefix" value="async-run-business-"/> <property name="waitForTasksToCompleteOnShutdown" value="true"/> <!-- 线程池对拒绝任务(无线程可用)的处理策略 ThreadPoolExecutor.CallerRunsPolicy ,直接在调用线程执行任务(保证业务逻辑正确). --> <property name="rejectedExecutionHandler"> <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/> </property> </bean>
Callable处理异步请求
- 什么是Callable?
Callable接口是java.util.concurrent下的一个接口,与Runnable接口类似,同样可以用来创建线程.Callable接口中也只有一个方法:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
与Runnable接口不同的是,Callable接口中的call()方法是一个带泛型的有返回值,抛出异常的方法.
- 请求流程:
- DispatcherServlet返回Callable
- Spring进行异步处理,将Callable提交到配置好的TaskExecutor中,使用一个隔离线程进行处理
- Servlet和所有的Filter退出web容器线程,但是保持响应开启状态
- Callable返回处理结果,Servlet再次被调用并恢复之前的处理
- MVC进行试图渲染
来一张MVC执行异步请求处理的时序图:
- MVC异步请求的优点:
由时序图可见,MVC的异步请求处理,可以将IO的操作交由独立的隔离线程去处理.空闲出web容器的线程,使web容器可以接收更多请求,可以支持更大的并发量.对于IO密集型项目,不失为一个好的优化方法. - Demo
@RestController
public class AsyncDemoController {
@GetMapping("/demo")
public Callable<String> asyncTest() {
System.out::println("主线程开始执行===>" + Thread.currentThread().getName() + "------" + System.currentTimeMillis());
Callable<String> call = this::showThread;
System.out::println("主线程执行完毕===>" + Thread.currentThread().getName() + "------" + System.currentTimeMillis());
return call;
}
private String showThread() throws InterruptedException {
System.out::println("副线程开始执行===>" + Thread.currentThread().getName() + "------" + System.currentTimeMillis());
Thread.sleep(1000);
System.out::println("副线程执行完毕===>" + Thread.currentThread().getName() + "------" + System.currentTimeMillis());
return "SUCCESS";
}
}
- 运行结果
主线程开始执行===>qtp1451546120-129------1549016869807
主线程执行完毕===>qtp1451546120-129------1549016869807
副线程开始执行===>async-run-business-1------1549016869814
副线程执行完毕===>async-run-business-1------1549016870815
从运行结果可以清楚看到,Servlet返回Callable,然后结束web容器线程.具体的业务执行交由隔离线程去执行.空闲出web容器线程去接收更多的请求,增强了服务器的并发能力.
文章参考:
SpringMVC之-Callable和DeferredResult异步请求
spring mvc对异步请求的处理
SpringMVC异步处理(Callable和DeferredResult)