异步servlet
在介绍Spring DeferredResult之前,我们需要先了解一下servlet3.0中的新特性——异步servlet。
本文不专门对其进行介绍,请参考其他人的博文
DeferredResult使用案例
springmvc的DeferredResult类结合了异步servlet的功能。
我们看一个使用案例
@RequestMapping("/test3")
@ResponseBody
public DeferredResult test3(){
//定义为局部变量,实现线程安全
final DeferredResult<User> dr = new DeferredResult<User>();
final User user= new User();
//executorService是项目中另外一个线程池
executorService.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
user.setId(11);
dr.setResult(user);
}
});
System.out.println("方法结束");
return dr;
}
当前端发起请求后,后端使用DeferredResult既可以将原先tomcat的线程空闲出来以接收其他请求,又可以将后端处理完的数据返回给前端,可谓一举两得。
而如果我们不用DeferredResult,后台端接收到请求后直接用新线程处理,那么由于起了新线程,方法直接执行到最后一句,前端无法获取到后端处理的数据。
DeferredResult原理解析
1.解析返回值DeferredResult
熟悉springmvc的人知道,请求入口是DispatcherServlet类。
我们忽略该类前面的代码,直接看后面返回值解析的过程
由于controller方法返回值为DeferredResult,则此时handler类是DeferredResultMethodReturnValueHandler。继续查看方法
进入startDeferredResultProcessing方法后,查看里面的核心代码
其中startAsyncProcessing是开启异步servlet的代码,前面有介绍过,此处就不仔细看了。
然后,进入后面的setResultHandler方法看看
方法参数传入一个函数式接口,并赋值给成员变量。
此时返回值解析过程结束了,同时由于异步servlet的特性,tomcat的连接也得到了释放。
2.给DeferredResult赋值
上述过程虽然释放了tomcat的连接,但其实用户请求还没有返回。
我们在线程池里使用了dr.setResult(user);
,现在看看该方法逻辑
回顾之前解析返回值时,我们传入了函数式接口并赋值给了成员,于是此时可以直接调用方法。
调用到如下方法
setConcurrentResultAndDispatch方法如下
红框处就是异步servlet的代码,当该段代码执行完成会发起一次新的请求到后台,又被DispatcherServlet类接收到(但是不会再进入controller了),最终将结果响应给客户端。
DeferredResult的应用场景
通过上述分析可知,DeferredResult释放了tomcat连接,将请求交给我们自己的线程池去处理,从而提高了并发量。
但是,由于异步servlet自身的特点,用户发起请求后还是会阻塞直到后台返回响应,因此DeferredResult并没有提高用户请求的响应速度;并且DeferredResult还使得后台多发了一次请求,增加了网络通信的消耗;同时,由于需要额外借助自定义线程池来处理请求,也增加了系统负担。
但是,在机器性能允许或者接口耗时很大的情况下,用DeferredResult的利大于弊。