异步Servlet和异步过虑器
异步处理功能可以节约容器线程。此功能的作用是释放正在等待完成的线程,是该线程能够被另一请求所使用。
要编写支持异步处理的 Servlet 或者过虑器,需要设置 asyncSupported 属性为 true。
@WebServlet(asyncSupported=true ... ) @WebFilter(asyncSupported=true ... )
当然也可以在部署描述符(web.xml)里面指定这个描述符。
<servlet> <servlet-name>AsyncServlet</servlet-name> <servlet-class>servlet.MyAsyncServlet</servlet-class> <async-supported>true</async-supported> </servlet>
注意,这个异步支持仅适合有一个长时间运行的任务并且要把运行结果通知给用户。如果你只有一个长期运行的任务,但用户并不需要知道处理结果,则可以提交一个 Runnable 该 Executor (执行器)并立即返回,
编写异步Servlet
(1)调用 ServletRequest 中的 startAsync 方法,该方法返回一个 AsyncContext 实例对象。
(2)调用 AsyncContext 的 setTimeout(),传递容器等待任务完成的超时时间的毫秒数。若不设置容器采用默认的超时时间,如果任务未能在指定的超时时间内完成,将会抛出一个超时异常。
(3)调用 asyncContext.start(),传递一个 Runnable 来执行一个长时间运行的任务。
(4)调用 Runnable 的 asyncContext.complete() 或 asyncContext.dispatch()方法来完成任务。
异步监听器
为支持 Servlet 和 过虑器配合执行异步操作,Servlet 3.0 增加了 asyncListener 接口用于接受异步处理过程中发生事件的通知。该接口提供了四个方法
void onStartAsync(AsyncEvent event) throws java.io.IOException // 异步操作启动完毕后调用该方法 void onComplete(AsyncEvent event) throws java.io.IOException // 异步操作完成后调用该方法 void onError(AsyncEvent event) throws java.io.IOException // 异步操作失败后调用该方法 void onTimeout(AsyncEvent event) throws java.io.IOException // 异步操作超时后调用该方法
异步事件类 AsyncEvent 提供了四个方法
public class AsyncEvent extends java.lang.Object // public AsyncContext getAsyncContext() // public ServletRequest getSuppliedRequest() // public ServletResponse getSuppliedResponse() // public java.lang.Throwable getThrowable() //
一个简单的异步调度的Servlet
package app11a; import java.io.IOException; 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(name = "AsyncDispatchServlet", urlPatterns = { "/asyncDispatch" }, asyncSupported = true ) public class AsyncDispatchServlet extends HttpServlet { private static final long serialVersionUID = 1L; public AsyncDispatchServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final AsyncContext asyncContext = request.startAsync(); // 调用 ServletRequest 中的 startAsync 方法 request.setAttribute("mainThread", Thread.currentThread().getName()); asyncContext.setTimeout(5000); // 调用 AsyncContext 的 setTimeout(),传递容器等待任务完成的超时时间的毫秒数 asyncContext.start(new Runnable() { // 调用 asyncContext.start(),传递一个 Runnable 来执行一个长时间运行的任务。 @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } request.setAttribute("workerThread", Thread.currentThread().getName()); asyncContext.dispatch("/threadNames.jsp"); // 调用 Runnable 的 asyncContext.complete() 或 asyncContext.dispatch()方法来完成任务 } }); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
<!-- threadNames.jsp --> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Asynchronous servlet</title> </head> <body> Main thread: ${mainThread } <br /> Worker thread: ${workerThread } </body> </html>
发送最新进度更新的异步servlet
package app11a; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class AsyncCompleteServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); final PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>Async Servlet</title>"); writer.println("<body>"); writer.println("<div id='progress'></div>"); final AsyncContext asyncContext = request.startAsync(); // 获取asynccontext对象 asyncContext.setTimeout(60000); // 设置超时 asyncContext.start(new Runnable() { // 开启一个线程 @Override public void run() { System.out.println("new thread: " + Thread.currentThread()); for (int i=0; i < 10; i++) { writer.println("<script>"); writer.println("document.getElementById('progress').innerHTML='" + (i * 10) + "% complete'"); writer.println("</script>"); writer.flush(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } writer.println("<script>"); writer.println("document.getElementById('progress').innerHTML = 'DONE'"); writer.println("</script>"); writer.println("</body>"); writer.println("</html>"); asyncContext.complete(); // } }); }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <display-name>app11a</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>AsyncComplete</servlet-name> <servlet-class>app11a.AsyncCompleteServlet</servlet-class> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>AsyncComplete</servlet-name> <url-pattern>/asyncComplete</url-pattern> </servlet-mapping> </web-app>
异步监听器的例子
package app11a; import java.io.IOException; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; public class MyAsyncListener implements AsyncListener { @Override public void onComplete(AsyncEvent asyncEvent) throws IOException { System.out.println("onComplete"); } @Override public void onError(AsyncEvent asyncEvent) throws IOException { System.out.println("onError"); } @Override public void onStartAsync(AsyncEvent asyncEvent) throws IOException { System.out.println("onStartAsync"); } @Override public void onTimeout(AsyncEvent asyncEvent) throws IOException { System.out.println("onTimeout"); } }
package app11a; import java.io.IOException; 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(name = "AsyncListenerServlet", urlPatterns = { "/asyncListener" }, asyncSupported = true) public class AsyncListenerServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final AsyncContext asyncContext = request.startAsync(); asyncContext.setTimeout(5000); asyncContext.addListener(new MyAsyncListener()); asyncContext.start(new Runnable() { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } String greeting = "hi from listener"; System.out.println("waiting ..."); request.setAttribute("greeting", greeting); asyncContext.dispatch("/test.jsp"); } }); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
<!-- test.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> This is the test.jsp
<br />
${greeting } </body> </html>
控制台输出