學習Servlet異步和非堵塞I/O

Asynchronous異步

  服務器資源是有價值的,應謹慎使用。考慮一個servlet 必須等待一個JDBC鏈接,或等待接收JMS消息 或從文件系統讀取的資源。等待一個「長期運行」過程返回會引發 線程徹底阻塞。異步
處理在在等待長時間運行的過程同時,能夠使控制(或線程)返回到容器來繼續執行其餘
任務。php

@WebServlet(urlPatterns="/async",asyncSupported=true)
public class MyAsyncServlet extends HttpServlet {html

java

也能夠在 web.xml定義<async-supported>爲true。web

也能夠在 web.xml定義<async-supported>爲true。服務器

而後,您能夠在單獨的線程使用request的同步放startA方法啓動異步處理
,此方法返回AsyncContext,它表明了 異步請求的執行上下文。而後你就能夠經過調用AsyncContext.complete完成異步 ,或者派遣到另外一個請求 資源(隱式)。容器將在後臺完成異步請求的調用。異步

假設有一個須要長時間運行的任務:async

class MyAsyncService implements Runnable {
AsyncContext ac;
public MyAsyncService(AsyncContext ac) {
this.ac = ac;
}
@Override
public void run() {
//. . .
ac.complete();
}
}ide

這個任務能夠在servlet中被調用異步運行:ui

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
  AsyncContext ac = request.startAsync();
  ac.addListener(new AsyncListener() {
    public void onComplete(AsyncEvent event)
      throws IOException {
       //. . .
  }

  public void onTimeout(AsyncEvent event)  throws IOException {
        //. . .
   }
  //. . .
  });this

  ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
  executor.execute(new MyAsyncService(ac));

}

該請求被放入異步模式。 當請求處理完成後,AsyncListener被註冊 偵聽事件,或已超時,或致使一個錯誤。長期運行的服務在一個單獨的線程異步調用,完成請求處理調用Context.complete。

 

非堵塞Nonblocking I/O

  Servlet 3.0中容許異步請求處理,但只容許傳統 I / O,這限制了應用程序的可擴展性。

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
  ServletInputStream input = request.getInputStream();
  byte[] b = new byte[1024];
  int len = -1;
  while ((len = input.read(b)) != -1) {
  //. . .
  }
}

若是傳入的數據流比服務器更慢,那麼 服務器線程就在等待數據。若是數據被寫入,相同的可能也會發生,這限制了Web容器的可擴展性。


  非阻塞I / O使得Web容器不只可伸縮,也能夠同時處理更多鏈接數量。非阻塞 I / O只能用異步請求處理的Servlet,過濾器。Servlet3.1實現了非阻塞I / O,經過引入兩個新的接口:ReadListener和WriteListener。這些監聽者有回調方法,能夠在 內容可被讀取或可寫入而不阻塞時調用。前面案例重寫爲:

AsyncContext context = request.startAsync();
ServletInputStream input = request.getInputStream();
input.setReadListener(new MyReadListener(input, context));

ReadListener有三個回調方法:

  • onDataAvailable回調方法是數據能夠被讀取時調用

  • onAllDataRead回調方法是當請求數據 徹底讀取時調用。

  • OnError回調是若是有一個錯誤處理請求時被調用

@Override
public void onDataAvailable() {
try {
 StringBuilder sb = new StringBuilder();
 int len = -1;
 byte b[] = new byte[1024];
 while (input.isReady() && (len = input.read(b)) != -1) {
    String data = new String(b, 0, len);
 }
 } catch (IOException ex) {
  //. . .
 }
}
@Override
public void onAllDataRead() {
 context.complete();
}
@Override
public void onError(Throwable t) {
 t.printStackTrace();
 context.complete();
}

ServletInputStream.isReady方法用於檢查數據能夠被讀取而不會阻塞,而後數據被讀出。 context.complete在 onAllDataRead和onError方法讀取數據的完成後調用。 Servle
tInputStream.isFinished能夠用來檢查一個非阻塞I/ O讀取的狀態。

WriteListener有兩個回調方法:

  • onWritePossible是能夠無堵塞寫入數據被調用時

  • onerror的是若是有錯誤處理響應時被調用

最多隻有一個WriteListener能夠在ServletOutputStream註冊。 ServletOut putStream.canWrite是一種新的方法檢查數據是否能夠不阻塞地被寫入。

相關文章
相關標籤/搜索