异步Servlet 的实现

使用场景:比较耗时的处理,因为比较耗时的处理使用同步servlet 会导致争锋web容器整体的处理能力地下,web容器的线程池的大小是固定的。我们可以把比较耗时的操作放到另外一个线程中去处理,此过程保留连接的请求和响应对象

servlet同步处理请求:


在这个处理过程中,处理过程越短,就能越快地将线程释放回线程池。但如果Servlet中的处理逻辑耗时越长,就会导致长期的占有Tomcat的处理 线程池,影响Tomcat的整体处理能力。




Servlet异步的实现:

当客户端请求到来时,首先通过管道,然后进入到wrapper容器的管道,调用servlet实现的service后,创建一个异步上下文将耗时的逻辑操作封装起来,交给用户自己定义的线程池。这时,Tomcat的处理线程马上回到Executor线程池,而不用等待耗时操作完成才释放线程,从而提升了Tomcat的整体处理能力,但是做完耗时操作后还需要对客户端响应,因此还需要持有Request和Response对象,以便输出响应报文到客户端。

AsyncContext简介:

在Servlet 3.0中,在ServletRequest上提供了 startAsync( )方法:

    AsyncContext startAsync() throws java.lang.IllegalStateException;   
      
    AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)   
    throws java.lang.IllegalStateException  
这两个方法都会传回AsyncContext介面的实作物件,前者会直接利用原有的请求与回应物件来建立AsyncContext ,后者可以让你传入自己建立的请求、回应包裹物件。   在呼叫了startAsync()方法取得AsyncContext物件之后,这次的回应会被延后,并释放容器所分配的执行绪。  


你可以通过AsyncContext的getRequest() 、 getResponse()方法取得请求、回应物件,此次对客户端的回应将暂缓至呼叫AsyncContext的complete()方法或dispatch()为止,前者表示回应完成,后者表示将回应调派给指定的URL  

若要能呼叫ServletRequest的startAsync()使用AsyncContext,你的 Servlet 必须能支援非同步处理,如果使用@WebServlet来标注,则可以设定其asyncSupportedtrue 。 例如:


AsyncListener简介:

Servlet 3.0为异步处理提供了一个监听器,使用AsyncListener接口表示。此接口负责管理异步事件,它可以监控如下四种事件:
1. 异步线程开始时,调用AsyncListener的onStartAsync(AsyncEvent event)方法;
2. 异步线程出错时,调用AsyncListener的onError(AsyncEvent event)方法;
3. 异步线程执行超时,则调用AsyncListener的onTimeout(AsyncEvent event)方法;
4. 异步执行完毕时,调用AsyncListener的onComplete(AsyncEvent event)方法;

要注册一个AsyncListener,只需将准备好的AsyncListener对象传递给AsyncContext对象的addListener()方法即可,如下所示:


@WebServlet(urlPatterns = "/asyncServlet", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	ScheduledThreadPoolExecutor userExecutor = new ScheduledThreadPoolExecutor(5);

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		AsyncContext ctx = req.startAsync(req, resp);
		ctx.addListener(new MyAsyncListener());
		userExecutor.execute(new AsyncHandler(ctx));
		resp.getWriter().write("正在执行异步回调");

	}

}


public class AsyncHandler implements Runnable {

	private AsyncContext ctx;

	public AsyncHandler(AsyncContext ctx) {
		this.ctx = ctx;
	}

	@Override
	public void run() {
		// 模拟耗时操作
		try {
			TimeUnit.SECONDS.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		try {
			PrintWriter pw = ctx.getResponse().getWriter();
			pw.println("done!!!!");
			pw.flush();
			pw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		ctx.complete();

	}

}
public class MyAsyncListener implements AsyncListener {

	@Override
	public void onComplete(AsyncEvent asyncEvent) throws IOException {
		try {
			AsyncContext asyncContext = asyncEvent.getAsyncContext();
			ServletResponse response = asyncContext.getResponse();
			PrintWriter out = response.getWriter();
			out.println("后台线程执行完成---【回调】");
			System.out.println("异步servlet【onComplete完成】");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onError(AsyncEvent asyncEvent) throws IOException {
		System.out.println("异步servlet错误");
	}

	@Override
	public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
		System.out.println("开始异步servlet");
	}

	@Override
	public void onTimeout(AsyncEvent asyncEvent) throws IOException {
		ServletRequest request = asyncEvent.getAsyncContext().getRequest();
		request.setAttribute("timeout", "true");
		System.out.println("异步servlet【onTimeout超时】");
	}

}


猜你喜欢

转载自blog.csdn.net/fd2025/article/details/80039440