17.12 異步處理

應用服務器的web容器一般會爲每一個請求分配一個服務線程。在重負載的場景下,容器須要大量的線程去服務全部客戶端請求。服務可擴展性的限制包括內存不足或者耗盡容器線程。建立可擴展web程序,你必須確保沒有關聯請求的線程是空閒的,因此容器可使用他們處理新請求。
這裏有兩個關聯請求的線程空閒的兩個場景:html

  • 線程須要在構建響應以前等待一個資源可用或者處理數據。例如,一個應用程序須要在構建響應前查詢數據庫或者經過遠程web服務訪問數據。
  • 線程須要在構建響應以前等待一個事件。例如,線程在構建響應以前須要等待一個JMS消息,另外一個客戶端的新信息,或者隊列裏面的新數據可用。

這些場景表明限制web程序可擴展性的阻塞操做。異步處理是指給這些阻塞操做分配一個新線程,並把關聯請求處理的線程返回給web容器。java

servlet 異步處理

java ee支持servlet和filter的異步處理。若是一個servlet或一個filter處理請求時可能到達一個阻塞操做,它能夠把操做分配給一個異步處理上下文而且在不生成響應的狀況下將關聯請求處理的線程回送給web容器。阻塞操做在不一樣線程的異步上下文中執行完成,它能夠生成響應或者轉發請求到另外一個servlet。
在一個servlet上啓用異步處理,設置@WebServlet註解的asyncSupported參數爲true,以下:web

@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet { ... }

javax.servlet.AsyncContext類提供在Service方法中執行異步處理所需的功能。得到一個AsyncContext實例,在service方法中調用request對象的startAsync()方法;例如:數據庫

public void doGet(HttpServletRequest req, HttpServletResponse resp) {
   ...
   AsyncContext acontext = req.startAsync();
   ...
}

這個調用將請求進入異步模式而且確保響應在退出service方法時每個月被提交。你必須在異步上下文完成阻塞操做時生成響應或者轉發到其餘servlet中。
AsyncContext類提供的基礎功能描述:
方法簽名:void start(Runnable run)
描述:容器提供的能提供阻塞操做處理的不一樣線程
方法簽名:ServletRequest getRequest()
描述:返回用來初始化異步上下文的請求。在上面的例子中,request與service方法中的相同。你能夠經過這個方法在異步上下文中從請求中獲取參數。
方法簽名:ServletResponse getResponse()
描述:返回初始化異步上下文的響應。在上面的例子中,response與service方法中的相同。你能夠在異步上下文中,使用這個方法寫入阻塞操做的結果到響應中。
方法簽名:void complete()
描述:完成異步操做,並關閉與此異步上下文關聯的響應。你能夠在異步上下文完成寫入響應後調用這個操做。
方法簽名:void dispatch(String path)
描述:轉發請求和響應到給定的路徑。在阻塞操做完成後,使用這個方法調用另外一個servlet寫出響應。服務器

等待一個資源

這個章節示範了怎麼使用AsyncContext上下文提供的功能,有以下用例:併發

  1. servlet從一個GET請求中獲取參數
  2. servlet使用一個資源,好比一個數據庫或者一個web service,基於這個參數獲取信息。這個資源可能比較緩慢,因此這多是一個阻塞操做。
  3. servlet使用資源的結果生成響應。

下面的例子是一個不使用異步處理的常見servlet:異步

@WebServlet(urlPatterns={"/syncservlet"})
public class SyncServlet extends HttpServlet {
   private MyRemoteResource resource;
   @Override
   public void init(ServletConfig config) {
      resource = MyRemoteResource.create("config1=x,config2=y");
   }

   @Override
   public void doGet(HttpServletRequest request, 
                     HttpServletResponse response) {
      response.setContentType("text/html;charset=UTF-8");
      String param = request.getParameter("param");
      String result = resource.process(param);
      /* ... print to the response ... */
   }
}

下面的示例是同一個servlet,但使用了異步處理:async

@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
   /* ... Same variables and init method as in SyncServlet ... */

   @Override
   public void doGet(HttpServletRequest request, 
                     HttpServletResponse response) {
      response.setContentType("text/html;charset=UTF-8");
      final AsyncContext acontext = request.startAsync();
      acontext.start(new Runnable() {
         public void run() {
            String param = acontext.getRequest().getParameter("param");
            String result = resource.process(param);
            HttpServletResponse response = acontext.getResponse();
            /* ... print to the response ... */
            acontext.complete();
   }
}

AsyncServlet在@WebServlet註解屬性中添加asyncSupported=true。其他的差別在service方法中:ide

  • request.startAsync() 引起request被異步執行;response不會在service方法結束時發送到客戶端;
  • acontext.start(new Runnable() {...})從容器中獲得一個新的線程。
  • 內部類中的run() 中的代碼在一個新線程中執行。內部類須要從異步上下文中讀取請求參數和寫入響應。調用異步上下文的complete()方法來提交併發送響應到客戶端。

AsyncServlet的service方法當即返回,同時請求在異步上下文中處理。url

相關文章
相關標籤/搜索