Servlet3异步原理
一、什么是異步Servlet
當一個新的請求到達時,Tomcat會從線程池里拿出一個線程來處理請求,這個線程會調(diào)用你的Web應用,Web應用在處理請求的過程中,Tomcat線程會一直阻塞,直到Web應用處理完畢才能再輸出響應,最后Tomcat才回收這個線程
假如你的Web應用需要較長的時間來處理請求(比如數(shù)據(jù)庫查詢或者等待下游的服務調(diào)用返回),那么Tomcat線程一直不回收,會占用系統(tǒng)資源,在極端情況下會導致線程饑餓,也就是說Tomcat沒有更多的線程來處理新的請求
那該如何解決這個問題呢?
Servlet3.0中引入的異步Servlet。主要是在Web應用里啟動一個單獨的線程來執(zhí)行這些比較耗時的請求,而Tomcat線程立即返回,不再等待Web應用將請求處理完,這樣Tomcat線程可以立即被回收到線程池,用來響應其他請求,降低了系統(tǒng)的資源消耗,同時還能提高系統(tǒng)的吞吐量
二、異步Servlet示例
SpringBoot啟動類添加@ServletComponentScan注解,掃描Servlet
@ServletComponentScan @SpringBootApplication public class AsyncServletApplication {public static void main(String[] args) {SpringApplication.run(AsyncServletApplication.class, args);}}異步Servlet:
@WebServlet(urlPatterns = {"/async"}, asyncSupported = true) public class AsyncServlet extends HttpServlet {//Web應用線程池,用來處理異步ServletExecutorService executor = Executors.newSingleThreadExecutor();@Overridepublic void service(HttpServletRequest req, HttpServletResponse resp) {//調(diào)用startAsync或者異步上下文AsyncContext asyncContext = req.startAsync();//添加AsyncListenerasyncContext.addListener(new AsyncServletListener());//用線程池來執(zhí)行耗時操作executor.execute(new Runnable() {@Overridepublic void run() {//在這里做耗時的操作try {asyncContext.getResponse().getWriter().println("Handling Async Servlet");} catch (IOException e) {}//異步Servlet處理完了調(diào)用異步上下文的complete方法asyncContext.complete();}});} }異步Servlet監(jiān)聽:
public class AsyncServletListener implements AsyncListener {private static final Logger LOGGER = LoggerFactory.getLogger(AsyncServletListener.class);/*** 異步線程執(zhí)行完畢時回調(diào)** @param asyncEvent* @throws IOException*/@Overridepublic void onComplete(AsyncEvent asyncEvent) throws IOException {LOGGER.info("AsyncServlet onComplete");}/*** 異步線程執(zhí)行超時回調(diào)** @param asyncEvent* @throws IOException*/@Overridepublic void onTimeout(AsyncEvent asyncEvent) throws IOException {LOGGER.info("AsyncServlet onTimeout");}/*** 異步線程執(zhí)行出錯回調(diào)** @param asyncEvent* @throws IOException*/@Overridepublic void onError(AsyncEvent asyncEvent) throws IOException {LOGGER.info("AsyncServlet onError");}/*** 異步線程開始執(zhí)行時回調(diào)** @param asyncEvent* @throws IOException*/@Overridepublic void onStartAsync(AsyncEvent asyncEvent) throws IOException {LOGGER.info("AsyncServlet onStartAsync");} }上面的代碼有三個要點:
-
通過注解的方式來注冊Servlet,除了@WebServlet注解,還需要加上asyncSupported=true的屬性,表明當前的Servlet是一個異步Servlet
-
Web應用程序需要調(diào)用Request對象的startAsync()方法來拿到一個異步上下文AsyncContext。這個上下文保存了請求和響應對象
-
Web應用需要開啟一個新線程來處理耗時的操作,處理完成后需要調(diào)用AsyncContext的complete()方法。目的是告訴Tomcat,請求已經(jīng)處理完成
雖然異步Servlet允許用更長的時間來處理請求,但是也有超時限制的,默認是30秒,如果30秒內(nèi)請求還沒處理完,Tomcat會觸發(fā)超時機制,向瀏覽器返回超時錯誤,如果這個時候你的Web應用再調(diào)用asyncContext.complete()方法,會得到一個IllegalStateException異常
三、異步Servlet原理
接收到Request請求之后,由Tomcat工作線程從HttpServletRequest中獲得一個異步上下文AsyncContext對象,然后由Tomcat工作線程把AsyncContext對象傳遞給業(yè)務處理線程,同時Tomcat工作線程歸還到工作線程池,這一步就是異步開始。在業(yè)務處理線程中完成業(yè)務邏輯的處理,生成response返回給客戶端
在Servlet3.0中雖然處理請求可以實現(xiàn)異步,但是InputStream和OutputStream的IO操作還是阻塞的,當數(shù)據(jù)量大的Request Body或者Response Body的時候,就會導致不必要的等待。從Servlet3.1以后增加了非阻塞IO,需要Tomcat8.x支持,通過在HttpServletRequest和HttpServletResponse中分別添加ReadListener和WriterListener方式,只有在IO數(shù)據(jù)滿足一定條件時(比如數(shù)據(jù)準備好時),才進行后續(xù)的操作
@WebServlet(urlPatterns = {"/async"}, asyncSupported = true) public class AsyncServlet extends HttpServlet {//Web應用線程池,用來處理異步ServletExecutorService executor = Executors.newSingleThreadExecutor();@Overridepublic void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {//調(diào)用startAsync或者異步上下文AsyncContext asyncContext = req.startAsync();//添加AsyncListenerasyncContext.addListener(new AsyncServletListener());ServletInputStream inputStream = req.getInputStream();inputStream.setReadListener(new ReadListener() {@Overridepublic void onDataAvailable() throws IOException {}@Overridepublic void onAllDataRead() throws IOException {//用線程池來執(zhí)行耗時操作executor.execute(new Runnable() {@Overridepublic void run() {//在這里做耗時的操作try {asyncContext.getResponse().getWriter().println("Handling Async Servlet");} catch (IOException e) {}//異步Servlet處理完了調(diào)用異步上下文的complete方法asyncContext.complete();}});}@Overridepublic void onError(Throwable throwable) {}});} }參考:
https://time.geekbang.org/column/article/106935
https://blog.csdn.net/wangxindong11/article/details/78591396
https://www.cnblogs.com/davenkin/p/async-servlet.html
總結(jié)
以上是生活随笔為你收集整理的Servlet3异步原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: servlet 工作原理
- 下一篇: mysql实现axure协同工作_Axu