17.13 非阻塞 I/O

應用程序的web容器一般爲每個客戶端請求分配一個服務端線程。開發可擴展的web應用,你必須確保關聯請求的線程是沒有空閒的,不須要等待一個阻塞操做完成。異步處理提供了在一個新線程處理阻塞操做的機制,把關聯請求的線程返回給容器。即便你全部的阻塞操做都在service方法中異步執行,關聯客戶端請求的線程基於input/output也可能出於暫時空閒狀態。
例如,若是一個客戶端在一個很慢的網絡鏈接上提交一個大的HTTP POST請求,server讀取請求的速度比客戶端上傳的速度快不少。使用TIO,容器關聯請求的線程有時會出於空閒狀態,由於要等待客戶端的請求的其他部分。
JAVA EE異步模式處理請求時,提供支持servlet和filter的NIO。下面的步驟總結了在service方法中如何使用NIO處理請求和寫出響應:java

1. 如 Asynchronous Processing章節描述,把請求設置爲異步模式  
2. 在service方法中從請求和響應對象中得到一個請求流或一個響應流
3. 分配給請求流一個監聽器或者分配給響應流一個監聽器 
4. 在監聽器的回調方法中處理請求和響應

NIO 支持類 javax.servlet.ServletInputStream

方法簽名:void setReadListener(ReadListener rl)
描述:將輸入流與包含回調方法的監聽器對象關聯,以異步讀取數據。提供的監聽器對象能夠是一個匿名類或者使用其餘的機制給監聽器對象傳入輸入流。
方法簽名:boolean isReady()
方法描述:若是數據能夠無阻塞讀取,返回true
方法簽名:boolean isFinished()
方法描述:當全部數據讀取完畢後,返回trueios

NIO 支持類 javax.servlet.ServletOutputStream

方法簽名:void setWriteListener(WriteListener wl)
方法描述:將此輸出流與包含回調方法的偵聽器對象關聯,以異步寫入數據。
您將寫入偵聽器對象提供爲匿名類,或使用其餘機制將輸出流傳遞給寫入偵聽器對象。
方法簽名:boolean isReady()
方法描述:若是數據能夠無阻塞寫入,返回trueweb

NIO監聽器支持接口

接口名稱:ReadListener
接口方法:void onDataAvailable()、void onAllDataRead()、void onError(Throwable t)
描述:ServletInputStream當數據能夠有效讀取、當數據讀取完畢、當發生一個錯誤時調用監聽器的這些方法。
接口名稱:WriteListener
接口方法:void onWritePossible()、void onError(Throwable t)
描述:ServletOutputStream當數據能夠無阻塞讀取、當發生一個錯誤時調用監聽器的這些方法。網絡

使用NIO讀取大的HTTP POST請求

本節代碼展現了在servlet對象中怎麼讀取一個大的HTTP POST數據,經過把請求放入異步模式中並使用NIO功能。app

@WebServlet(urlPatterns={"/asyncioservlet"}, asyncSupported=true)
public class AsyncIOServlet extends HttpServlet {
   @Override
   public void doPost(HttpServletRequest request, 
                      HttpServletResponse response)
                      throws IOException {
      final AsyncContext acontext = request.startAsync();
      final ServletInputStream input = request.getInputStream();
      
      input.setReadListener(new ReadListener() {
         byte buffer[] = new byte[4*1024];
         StringBuilder sbuilder = new StringBuilder();
         @Override
         public void onDataAvailable() {
            try {
               do {
                  int length = input.read(buffer);
                  sbuilder.append(new String(buffer, 0, length));
               } while(input.isReady());
            } catch (IOException ex) { ... }
         }
         @Override
         public void onAllDataRead() {
            try {
               acontext.getResponse().getWriter()
                                     .write("...the response...");
            } catch (IOException ex) { ... }
            acontext.complete();
         }
         @Override
         public void onError(Throwable t) { ... }
      });
   }
}

此示例使用@WebServlet批註參數asyncSupported = true聲明具備異步支持的Web Servlet。服務方法首先經過調用請求對象的startAsync()方法將請求置於異步模式,這是使用非阻塞I / O所必需的。而後,服務方法得到與請求相關聯的輸入流,並分配定義爲內部類的讀取偵聽器。偵聽器在可用時讀取部分請求,而後在完成讀取請求時將一些響應寫入客戶端。異步

相關文章
相關標籤/搜索