服務器資源是有價值的,應謹慎使用。考慮一個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。
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是一種新的方法檢查數據是否能夠不阻塞地被寫入。