1. 程式人生 > >異步處理支持

異步處理支持

pub 文件 catch pos alt timeout get 必須 子線程

Servlet 3.0 之前,一個普通 Servlet 的主要工作流程大致如下:首先,Servlet 接收到請求之後,可能需要對請求攜帶的數據進行一些預處理;接著,調用業務接口的某些方法,以完成業務處理;最後,根據處理的結果提交響應,Servlet 線程結束。其中第二步的業務處理通常是最耗時的,這主要體現在數據庫操作,以及其它的跨網絡調用等,在此過程中,Servlet 線程一直處於阻塞狀態,直到業務方法執行完畢。在處理業務的過程中,Servlet 資源一直被占用而得不到釋放,對於並發較大的應用,這有可能造成性能的瓶頸。對此,在以前通常是采用私有解決方案來提前結束 Servlet 線程,並及時釋放資源。

Servlet 3.0 針對這個問題做了開創性的工作,現在通過使用 Servlet 3.0 的異步處理支持,之前的 Servlet 處理流程可以調整為如下的過程:首先,Servlet 接收到請求之後,可能首先需要對請求攜帶的數據進行一些預處理;接著,Servlet 線程將請求轉交給一個異步線程來執行業務處理,線程本身返回至容器,此時 Servlet 還沒有生成響應數據,異步線程處理完業務以後,可以直接生成響應數據(異步線程擁有 ServletRequest 和 ServletResponse 對象的引用),或者將請求繼續轉發給其它 Servlet。如此一來, Servlet 線程不再是一直處於阻塞狀態以等待業務邏輯的處理,而是啟動異步線程之後可以立即返回。

異步處理特性可以應用於 Servlet 和過濾器兩種組件,由於異步處理的工作模式和普通工作模式在實現上有著本質的區別,因此默認情況下,Servlet 和過濾器並沒有開啟異步處理特性,如果希望使用該特性,則必須按照如下的方式啟用:

  1. 對於使用傳統的部署描述文件 (web.xml) 配置 Servlet 和過濾器的情況,Servlet 3.0 為 <servlet> 和 <filter> 標簽增加了 <async-supported> 子標簽,該標簽的默認取值為 false,要啟用異步處理支持,則將其設為 true 即可。以 Servlet 為例,其配置方式如下所示:
    1 2 3 4 5 <servlet> <servlet-name>DemoServlet</servlet-name> <servlet-class>footmark.servlet.Demo Servlet</servlet-class> <async-supported>true</async-supported> </servlet>
  2. 對於使用 Servlet 3.0 提供的 @WebServlet 和 @WebFilter 進行 Servlet 或過濾器配置的情況,這兩個註解都提供了 asyncSupported 屬性,默認該屬性的取值為 false,要啟用異步處理支持,只需將該屬性設置為 true 即可。以 @WebFilter 為例,其配置方式如下所示:
1 2 @WebFilter(urlPatterns = "/demo",asyncSupported = true) public class DemoFilter implements Filter{...}

一個簡單的模擬異步處理的 Servlet 示例如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @WebServlet(urlPatterns = "/demo", asyncSupported = true) public class AsyncDemoServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { resp.setContentType("text/html;charset=UTF-8"); PrintWriter out = resp.getWriter(); out.println("進入Servlet的時間:" + new Date() + "."); out.flush(); //在子線程中執行業務調用,並由其負責輸出響應,主線程退出 AsyncContext ctx = req.startAsync(); new Thread(new Executor(ctx)).start(); out.println("結束Servlet的時間:" + new Date() + "."); out.flush(); } } public class Executor implements Runnable { private AsyncContext ctx = null; public Executor(AsyncContext ctx){ this.ctx = ctx; } public void run(){ try { //等待十秒鐘,以模擬業務方法的執行 Thread.sleep(10000); PrintWriter out = ctx.getResponse().getWriter(); out.println("業務處理完畢的時間:" + new Date() + "."); out.flush(); ctx.complete(); } catch (Exception e) { e.printStackTrace(); } } }

除此之外,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() 方法即可,如下所示:

1 2 3 4 5 6 7 AsyncContext ctx = req.startAsync(); ctx.addListener(new AsyncListener() { public void onComplete(AsyncEvent asyncEvent) throws IOException { // 做一些清理工作或者其他 } ... });

異步處理支持