有時候我們的請求需要很長時間的處理,可能是複雜的運算或是很大包的資源,這種時後就會造成執行緒長時間被佔用,久了久有可能拖慢整體的效能,甚至影響到一般的操作。
這種時候我們應該保留 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 的操作就好了!)
其實寫到這邊,已經開始沒有一個主軸了,而是一個一個小節的瞭解 Servlet 可以操作的項目,寫起來跟看起來就比較有種工具書的感覺。
在我想到下一階段的主題前,大家就忍耐忍耐囉~~~