servlet3.0异步servlet

来源:互联网 发布:零基础不懂java难学吗 编辑:程序博客网 时间:2024/05/16 02:10

使用异步servlet主要原因就是因为,在service方法中业务逻辑如果碰到io操作时间比较长的操作,这样这个service方法就会长时间占用tomcat容器线程池中的线程,这样是不利于其他请求的处理的,当线程池中的线程处理任务时,任务由于长时间io操作,肯定会阻塞线程处理其他任务,引入异步servlet的目的就是将容器线程池和业务线程池分离开。在处理大io的业务操作的时候,把这个操作移动到业务线程池中进行,释放容器线程,使得容器线程处理其他任务,在业务逻辑执行完毕之后,然后在通知tomcat容器线程池来继续后面的操作,这个操作应该是把处理结果commit到客户端或者是dispatch到其他servlet上。


    上图是原始servlet和容器线程池的模型,下图是异步servlet,容器线程池和业务线程池的模型,从图中可以看出,原始模型在处理业务逻辑的过程中会一直占有容器线程池,而异步servlet模型,可以看出在业务线程池处理的过程中,有一段时间容器线程池中的那个线程是空闲的,这种设计大大提高了容器的处理请求的能力。

    异步servlet的开启在service中开启,对于一般请求,在service方法之后,都会commit response结果到客户端,但是在异步servlet中这个commit是没有意义的,因为输出还没产生,在业务线程池中还未处理完毕,这时需要把当前处理环境保存起来,以便业务线程池处理完毕后,再次找到这个处理环境继续处理。

    下面来看一个异步servlet的实例:

package com.journaldev.servlet.async;import java.io.IOException;import java.util.concurrent.ThreadPoolExecutor;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;public class AsyncServlet extends HttpServlet {private static final long serialVersionUID = 1L;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 secondsif (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 AsyncRequestProcessorThread(asyncCtx, secs));long endTime = System.currentTimeMillis();System.out.println("AsyncLongRunningServlet End::Name="+ Thread.currentThread().getName() + "::ID="+ Thread.currentThread().getId() + "::Time Taken="+ (endTime - startTime) + " ms.");request.getRequestDispatcher("/index.jsp").forward(request, response);}}
异步监听器类:

package com.journaldev.servlet.async;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.AsyncEvent;import javax.servlet.AsyncListener;import javax.servlet.ServletResponse;import javax.servlet.annotation.WebListener;public class AppAsyncListener implements AsyncListener {@Overridepublic void onComplete(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onComplete");// we can do resource cleanup activity here}@Overridepublic void onError(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onError");//we can return error response to client}@Overridepublic void onStartAsync(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onStartAsync");//we can log the event here}@Overridepublic void onTimeout(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onTimeout");//we can send appropriate response to clientServletResponse response = asyncEvent.getAsyncContext().getResponse();PrintWriter out = response.getWriter();out.write("TimeOut Error in Processing");}}

线程池初始化类

package com.journaldev.servlet.async;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;public class AppContextListener implements ServletContextListener {public void contextInitialized(ServletContextEvent servletContextEvent) {// create the thread poolThreadPoolExecutor 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();}}

异步执行大量IO的线程类

package com.journaldev.servlet.async;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.AsyncContext;public class AsyncRequestProcessorThread implements Runnable {private AsyncContext asyncContext;private int secs;public AsyncRequestProcessorThread() {}public AsyncRequestProcessorThread(AsyncContext asyncCtx, int secs) {this.asyncContext = asyncCtx;this.secs = secs;}@Overridepublic 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 processingasyncContext.complete();}private void longProcessing(int secs) {// wait for given time before finishingtry {Thread.sleep(secs);} catch (InterruptedException e) {e.printStackTrace();}}}

工程代码见附件:http://download.csdn.net/detail/shanhuhau/6994813

执行下面两个URL

http://localhost:8080/asyncServlet/LongRunningServlet?time=1000

http://localhost:8080/asyncServlet/async?time=1000

发现对于用户来说,响应时间是没有变化的,变化的是后台占用servlet主线程的事件,时间缩短了很多,这样servlet就能处理更多的请求,而不必在大量IO操作的时候等待

参考:http://blog.chinaunix.net/uid-27767798-id-3806685.html

http://blog.csdn.net/kuyuyingzi/article/details/18843487


    
2 0
原创粉丝点击