前言
通過我之前的Tomcat系列文章,相信看我博客的同學對Tomcat應該有一個比較清晰的了解了,在前幾篇博客我們討論了Tomcat在SpringBoot框架中是如何啟動的,討論了Tomcat的內部組件是如何設計以及請求是如何流轉的,那么我們這邊博客聊聊Tomcat的異步Servlet,Tomcat是如何實現異步Servlet的以及異步Servlet的使用場景。
手擼一個異步的Servlet
我們直接借助SpringBoot框架來實現一個Servlet,這里只展示Servlet代碼:
@WebServlet(urlPatterns = "/async",asyncSupported = true)@Slf4jpublic class AsyncServlet extends HttpServlet { ExecutorService executorService =Executors.newSingleThreadExecutor(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //開啟異步,獲取異步上下文 final AsyncContext ctx = req.startAsync(); // 提交線程池異步執行 executorService.execute(new Runnable() { @Override public void run() { try { log.info("async Service 準備執行了"); //模擬耗時任務 Thread.sleep(10000L); ctx.getResponse().getWriter().print("async servlet"); log.info("async Service 執行了"); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } //最后執行完成后完成回調。 ctx.complete(); } }); }
上面的代碼實現了一個異步的Servlet,實現了 doGet
方法注意在SpringBoot中使用需要再啟動類加上 @ServletComponentScan
注解來掃描Servlet。既然代碼寫好了,我們來看看實際運行效果。
我們發送一個請求后,看到頁面有響應,同時,看到請求時間花費了10.05s,那么我們這個Servlet算是能正常運行啦。有同學肯定會問,這不是異步servlet嗎?你的響應時間并沒有加快,有什么用呢?對,我們的響應時間并不能加快,還是會取決于我們的業務邏輯,但是我們的異步servlet請求后,依賴于業務的異步執行,我們可以立即返回,也就是說,Tomcat的線程可以立即回收,默認情況下,Tomcat的核心線程是10,最大線程數是200,我們能及時回收線程,也就意味著我們能處理更多的請求,能夠增加我們的吞吐量,這也是異步Servlet的主要作用。
異步Servlet的內部原理
了解完異步Servlet的作用后,我們來看看,Tomcat是如何是先異步Servlet的。其實上面的代碼,主要核心邏輯就兩部分, final AsyncContext ctx = req.startAsync();
和 ctx.complete();
那我們來看看他們究竟做了什么?
public AsyncContext startAsync(ServletRequest request, ServletResponse response) { if (!isAsyncSupported()) { IllegalStateException ise = new IllegalStateException(sm.getString("request.asyncNotSupported")); log.warn(sm.getString("coyoteRequest.noAsync", StringUtils.join(getNonAsyncClassNames())), ise); throw ise; } if (asyncContext == null) { asyncContext = new AsyncContextImpl(this); } asyncContext.setStarted(getContext(), request, response, request==getRequest() && response==getResponse().getResponse()); asyncContext.setTimeout(getConnector().getAsyncTimeout()); return asyncContext; }
新聞熱點
疑難解答