Servlet Async 非同步動作
Servlet Async 非同步動作
有時候我們的請求需要很長時間的處理,可能是複雜的運算或是很大包的資源,這種時後就會造成執行緒長時間被佔用,久了久有可能拖慢整體的效能,甚至影響到一般的操作。
這種時候我們應該保留 Servlet 的資源,讓 Servlet 有資源分配給其他請求,等到長時間的處理資料處理完成後再回覆給客戶就好了!
而如何保留資源呢?就是丟出一個一個執行緒去外面囉! 讓我們看看 Servlet 中要怎麽實作吧!
Servlet 中 ServletRequest 提供了一個方法叫做 startAsync() 方法,會回傳 AsyncContext 物件,在取得 AsyncContext 之後,我們的回應會被延後,資源也就釋放回 Servlet 囉!
在使用之前,我們必須讓我們的 Servlet 物件支援 async 支援方法是在 @WebServlet 中設定
@WebServlet(
urlPatterns = "/async",
asyncSupported = true
)
設定過後就可以在 Servlet 物件中使用 Async 方法了!
@WebServlet(
urlPatterns = "/async",
asyncSupported = true
)
public class AsyncServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/plan; charset=UTF8");
AsyncContext asyncContext = request.startAsync(); // 開始非同步動作
// 從 AsyncContext 內取得 Response 物件後,回應字串出去
asyncContext.getResponse().getWriter().println("Hello Async")
// 記得一定要跟它說完成了
// 不然就算結束了 頁面那端也完全不知道他結束了
asyncContext.complete();
}
}
這樣其實就是 Async 方法了,只是我們沒有做任何需要丟出去運算的事情而已 接下來就讓我們簡單丟一個執行緒出去試試看吧!!!
一樣拿剛剛的 AsyncServlet 來改
@WebServlet(
urlPatterns = "/async",
asyncSupported = true
)
public class AsyncServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/plan; charset=UTF8");
AsyncContext asyncContext = request.startAsync(); // 開始非同步動作
doAsync()
.thenApplyAsync(s -> s + "讓你久等了")
.whenComplete((ok, err) -> {
try {
asyncContext.getResponse().getWriter().println(ok); // 把 ok 的訊息印出來;因為只是測試 就先不處理 err 了!
asyncContext.complete(); // 記得一定要跟它說完成了 不然就算結束了 在頁面上看起來也是沒有結束
} catch (IOException e) {
e.printStackTrace();
}
});
// asyncContext.complete(); 不可以放在這裡哦,如果放這裡會在 async 執行完之前,就被這裡的 complete() 給跳掉
// --- 補充 ---
// 如果沒有特別傳傳送其他 Response 進去
// 我們原先的 response 跟 asyncContext.getResponse() 是一摸一樣的東西哦!
// 只是都用 async 了,我覺得用 asyncContext.getResponse() 會比較清楚自己現在在 async 中~
// System.out.println(asyncContext.getResponse());
// System.out.println(response);
// System.out.println(asyncContext.getResponse() == response);
}
/**
* 非同步動作
* @return
*/
private CompletableFuture<String> doAsync() {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000); // 等個三秒
return "I'm back!!";
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
}
( 這邊很多的操作跟 Servlet 比較沒關係,執行緒的操作本來就比較困難所以看不懂也沒有關係,我們就主要聚焦於 AsyncContext 的操作就好了! )