有的时候servlet在相应报文以前会有一些耗时操做,好比JDBC的操做,或者等待另外一个远程Web的响应,同步Servlet中等待阻塞会致使Web容器总体的处理能力低下。对于这种状况可以使用servlet异步处理方式,把比较耗时的操做能够放置到另一个线程中进行处理,此过程保留链接的请求和响应对象,在处理完成以后能够把处理的结果通知到客户端。java
如图所示,Tomcat的客户端请求由管道处理最后会经过Wrapper容器的管道,这时它会调Servlet实例的service方法进行逻辑处理,处理完后响应客户端,整个处理由Tomcat的Executor线程池的线程处理。apache
tomcat
缺点:碰见处理逻辑耗时较长的任务会长时间的占用tomcat的处理线程,影响tomcat的总体处理能力。特别是当你的线程数有限好比只有5个,同时碰见5个耗时100ms的请求,那么在这100ms内的其它请求都会失败,严重影响的服务的吞吐量和稳定性。app
优势:实现逻辑简单易懂,对于大量的耗时短的任务性能好。异步
异步Servlet是在3.0版本引入的,客户端请求到来,而后经过管道最后进入到Wrapper容器的管道,调用Servlet实例的service后,建立一个异步上下文将耗时的逻辑操做封装起来,交给用户本身定义的线程池,这时Tomcat的处理线程就能立刻回到Executor线程池,而不用等待耗时的操做完成才让出线程,从而提高了Tomcat的总体处理能力。async
ide
初始化线程池性能
@WebListener
public class AppContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
// create the thread pool
ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));
servletContextEvent.getServletContext().setAttribute("executor",
executor);
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) servletContextEvent
.getServletContext().getAttribute("executor");
executor.shutdown();
}
}
复制代码
编写一个监听器,监听异步任务生命周期中的一些动做。this
public class AppAsyncListener implements AsyncListener {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onComplete");
// we can do resource cleanup activity here
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onError");
//we can return error response to client
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onStartAsync");
//we can log the event here
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onTimeout");
//we can send appropriate response to client
ServletResponse response = asyncEvent.getAsyncContext().getResponse();
PrintWriter out = response.getWriter();
out.write("TimeOut Error in Processing");
}
}
复制代码
具体的业务逻辑放在一个Runable的实现类中url
public class AsyncRequestProcessor implements Runnable {
private AsyncContext asyncContext;
private int secs;
public AsyncRequestProcessor() {
}
public AsyncRequestProcessor(AsyncContext asyncCtx, int secs) {
this.asyncContext = asyncCtx;
this.secs = secs;
}
@Override
public void run() {
System.out.println("Async Supported? "
+ asyncContext.getRequest().isAsyncSupported());
longProcessing(secs);
try {
PrintWriter out = asyncContext.getResponse().getWriter();
out.write("Processing done for " + secs + " milliseconds!!");
} catch (IOException e) {
e.printStackTrace();
}
//complete the processing
asyncContext.complete();
}
private void longProcessing(int secs) {
// wait for given time before finishing
try {
Thread.sleep(secs);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
最后,Async Servlet 实现
@WebServlet(urlPatterns = "/AsyncLongRunningServlet", asyncSupported = true)
public class AsyncLongRunningServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
System.out.println("AsyncLongRunningServlet Start::Name="
+ Thread.currentThread().getName() + "::ID="
+ Thread.currentThread().getId());
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
String time = request.getParameter("time");
int secs = Integer.valueOf(time);
// max 10 seconds
if (secs > 10000) {
secs = 10000;
}
AsyncContext asyncCtx = request.startAsync();
asyncCtx.addListener(new AppAsyncListener());
asyncCtx.setTimeout(9000);
ThreadPoolExecutor executor = (ThreadPoolExecutor) request
.getServletContext().getAttribute("executor");
executor.execute(new AsyncRequestProcessor(asyncCtx));
long endTime = System.currentTimeMillis();
System.out.println("AsyncLongRunningServlet End::Name="
+ Thread.currentThread().getName() + "::ID="
+ Thread.currentThread().getId() + "::Time Taken="
+ (endTime - startTime) + " ms.");
}
}
复制代码
PS:在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 能够直接经过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其余代码。