自从Servlet3推出异步之后,Spring3.2已经开始增加了对Servlet异步的支持。
在高并发的Web场景中,如果出现处理请求的过程中出现较长的阻塞,对整个服务的性能影响是致命的,大量的请求长时间无返回会占用Servlet容器的工作线程,造成容器线程池耗尽,从而无法对外提供服务。
那么Servlet异步的初衷就是来解耦Servlet线程和我们应用的具体业务逻辑线程的,从而把Servlet请求线程(即Servlet容器的dispatch线程)快速返回给容器,以便给其它请求提供服务。下面这张图展示的比较清楚(图片来源于网络)
今天在这里主要给大家讲解下Spring中使用异步servlet的方式,以及实现原理。
使用方式
下面边张图便是Spring中使用异步请求的方式
我们看到,与普通的Controller方法的区别在于,handler方法返回的是Callable(还有其它的方式如:返回DeferredResult等),那么返回一个Callable就怎么成异步的了呢,我们得继续往底层看下Spring的源码
实现原理
在Spring中,处理servlet请求的核心代码在这里
方法第一行是真正执行业务逻辑的方法(也就是Controller里具体方法内容),在这一步,不管是否是异步请求,都要执行这一步,那么你可能要问,那异步的处理在哪里呢?先别急,你想想看,我们的异步方法返回的什么?没错,是Callable,而我们真正的业务逻辑是在Callable内部的call()中,所以现在只是返回一个callable对象,call()并没有调用。那么何时才真正调用call()中真正的业务逻辑呢,继续看上图
这一行恰恰是关键,顺便说下,在Spring中,有一系列的HandlerReturnValueHandler接口的实现,它们都是用来处理Controller中handler方法返回值的。在这里,我们异步方法的返回值是Callable,那就找Callable相关的返回值处理器,是这个类:
具体执行方法如下:
上图选中的一行,正是异步的关键,继续往里跟:
我们看到,我们定义的callable被交给了一个executor执行,此时原有的Servlet请求线程就返回了(返回的同时ServletResponse并没有被提交)。
接下来呢,异步任务完了之后何时返回给用户呢?
我们看上边run()方法里的最后一行
执行到这一行,异步任务已经被执行完了,这一行的作用就是把剩下来的事又交给容器,让容器把结果返回给客户端。
这里有一个关键问题是,怎么返回给容器,容器又怎么知道把结果给谁呢
我们看Servlet3中的ServletRequest中有一个方法,也就是请求转为异步的方法
它会返回一个AsyncContext,这个接口正是整个异步请求生命周期的关键。
它持有异步请求中的关键上下文,也就是request,reponse
此外还有两个重要的方法
上边说到异步任务执行完之后,把结果返回给Servlet容器,就是通过dispatch()方法完成的,具体的dispatch实现,不同的Servlet容器有不同的实现。
总结
Servlet3异步请求该用在什么样的场景呢?引用Servlet3规范中的原话:
Some times a filter and/or servlet is unable to complete the processing of a request without waiting for a resource or event before generating a response. For example, a servlet may need to wait for an available JDBC connection, for a response from a remote web service, for a JMS message, or for an application event, before proceeding to generate a response. Waiting within the servlet is an inefficient operation as it is a blocking operation that consumes a thread and other limited resources. Frequently a slow resource such as a database may have many threads blocked waiting for access and can cause thread starvation and poor quality of service for an entire web container.
也就是说,后端应用中有大IO事务的情况,采用异步Servlet是明智的选择。
欢迎关注,期待与您的交流,让我们携手在通往牛逼的小路上徐徐前行。