异步servlet总结java
服务端须要tomcat8.0及以上web
jdk须要8及以上tomcat
须要显式开启异步servlet配置,方法有两种:并发
<servlet>异步
<servlet-name>DemoServlet</servlet-name>async
<servlet-class>footmark.servlet.Demo Servlet</servlet-class>ide
<async-supported>true</async-supported>函数
</servlet>this
@WebFilter(urlPatterns = "/demo",asyncSupported = true)编码
static private ScheduledThreadPoolExecutor userExecutor =
new ScheduledThreadPoolExecutor(5);
C、 在run方法中,从异步上下文环境中获得request和response,编码实现耗时的应用逻辑,以后经过respones将下行报文返回给客户端
public class AsyncHandler implements Runnable{
private AsyncContext ctx;
public AsyncHandler(AsyncContext ctx) {
this.ctx = ctx;
}
public void run() {
PrintWriter pw;
try {
pw = ctx.getResponse().getWriter();
pw.print("done");
pw.flush();
pw.close();
} catch(IOException e) {
e.printStackTrace();
}
ctx.complete();
}
}
AsyncContext aCtx = request.startAsync(request,response);
userExecutor.execute(new AsyncHandler(aCtx));
一个应用逻辑在tomcat中所需的处理时间分两块:等待进入tomcat内部线程池上运行的时间和应用的真实处理时间(实际上不止这两块时间,但此处为了方便分析作了简化)。
tomcat内部线程池的线程数量是有限的,若是应用的执行时间很长,就会长时间占用线程,使得线程不能很快地回到线程池。这样新的请求可能由于线程池中没有就绪的线程而不得不长时间的等待。这一方面会使得tomcat的并发数不能获得提高,另外一方面使得用户体验不够好。
为了解决这个问题,能够在servlet内部新建一个内部线程池,当新的请求到来以后,会在tomcat内部的线程池中取出一个线程运行,但这个tomcat的线程并不真正处理那些耗时的业务逻辑,而是启动一个异步上下文,并将那些耗时的业务逻辑委托给servlet内部线程池去运行。tomcat的线程委托完以后本身就能够很快的返回tomcat的线程池中,以待新的请求到来。
servlet内部的线程池处理完耗时的业务请求以后,须要返回下行报文给客户端,因此它须要reqeust对象和resoponse对象,这就是为何须要给异步handler传入异步上下文的缘由---------在异步上下文中,能够获得request和response对象。
异步servlet技术并不能缩短应用逻辑处理的时间,这个时间不管如何是无法缩短的。然而,经过将应用逻辑的执行委托给servlet内部的线程池,tomcat的线程池就不须要本身亲自去执行这些耗时的逻辑了,所以tomcat的执行线程能够很快地返回本身的线程池中,这带来两个好处:tomcat能够处理更多的请求,换而言之就是提升了系统的并发量;新的请求可能无需等待就能及时被tomcat内部的线程池委托到servlet的内部线程池上,这个可能的等待时间被消灭了,换而言之就是从总体上减小了系统的响应时间。
但要说明的是,应用那些耗时的逻辑,不管是直接在tomcat内部线程池上运行,仍是在servlet的内部现场池上运行,这个时间是没法缩短的。
源码
一、AsyncHandler.java
package com.nari;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
public class AsyncHandler implements Runnable{
private AsyncContext ctx;
public AsyncHandler(AsyncContext ctx) {
this.ctx = ctx;
}
@Override
public void run() {
PrintWriter pw;
try {
pw = ctx.getResponse().getWriter();
pw.print("done");
pw.flush();
pw.close();
} catch(IOException e) {
e.printStackTrace();
}
ctx.complete();
}
}
2.AsyncServlet.java
package com.nari;
import java.io.IOException;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = "/AsyncServlet", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
static private ScheduledThreadPoolExecutor userExecutor = new ScheduledThreadPoolExecutor(5);
public AsyncServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext aCtx = request.startAsync(request,response);
userExecutor.execute(new AsyncHandler(aCtx));
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}