Servlet 3的异步Servlet功能
 在深入了解什么是異步Servlet之前,讓我們嘗試了解為什么需要它。 假設我們有一個Servlet,處理時間很長,如下所示。 
LongRunningServlet.java
package com.journaldev.servlet;import java.io.IOException; import java.io.PrintWriter;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;@WebServlet("/LongRunningServlet") public class LongRunningServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {long startTime = System.currentTimeMillis();System.out.println("LongRunningServlet Start::Name="+ Thread.currentThread().getName() + "::ID="+ Thread.currentThread().getId());String time = request.getParameter("time");int secs = Integer.valueOf(time);// max 10 secondsif (secs > 10000)secs = 10000;longProcessing(secs);PrintWriter out = response.getWriter();long endTime = System.currentTimeMillis();out.write("Processing done for " + secs + " milliseconds!!");System.out.println("LongRunningServlet Start::Name="+ Thread.currentThread().getName() + "::ID="+ Thread.currentThread().getId() + "::Time Taken="+ (endTime - startTime) + " ms.");}private void longProcessing(int secs) {// wait for given time before finishingtry {Thread.sleep(secs);} catch (InterruptedException e) {e.printStackTrace();}}}如果我們通過瀏覽器在URL上方為http://localhost:8080/AsyncServletExample/LongRunningServlet?time=8000 servlet上方收到響應,則響應為“處理完成8000毫秒!” 8秒后。 現在,如果您查看服務器日志,將得到以下日志:
LongRunningServlet Start::Name=http-bio-8080-exec-34::ID=103 LongRunningServlet Start::Name=http-bio-8080-exec-34::ID=103::Time Taken=8002 ms.因此,盡管大多數處理與Servlet請求或響應無關,但我們的Servlet線程運行了大約8+秒。
這可能導致線程匱乏 –由于在所有處理完成之前我們的Servlet線程被阻塞,因此如果服務器收到大量要處理的請求,它將達到servlet線程的最大限制,并且進一步的請求將出現Connection Refused錯誤。
在Servlet 3.0之前,有針對這些長時間運行的線程的特定于容器的解決方案,我們可以生成單獨的工作線程來執行繁重的任務,然后將響應返回給客戶端。 啟動工作線程后,Servlet線程將返回到Servlet池。 Tomcat的Comet,WebLogic的FutureResponseServlet和WebSphere的異步請求分派器是異步處理實現的一些示例。
特定于容器的解決方案的問題在于,在不更改應用程序代碼的情況下我們無法移至其他servlet容器,這就是為什么在Servlet 3.0中添加了Async Servlet支持以為Servlet中的異步處理提供標準方式的原因。
異步Servlet實現
讓我們看一下實現異步servlet的步驟,然后為上述示例提供異步支持的servlet。
一旦我們完成了異步Servlet示例示例的項目,它將如下圖所示。
在Servlet上下文偵聽器中初始化輔助線程池
AppContextListener.java
package com.journaldev.servlet.async;import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener;@WebListener public class AppContextListener implements ServletContextListener {public void contextInitialized(ServletContextEvent servletContextEvent) {// create the thread poolThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L,TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));servletContextEvent.getServletContext().setAttribute("executor",executor);}public void contextDestroyed(ServletContextEvent servletContextEvent) {ThreadPoolExecutor executor = (ThreadPoolExecutor) servletContextEvent.getServletContext().getAttribute("executor");executor.shutdown();}}該實現非常簡單,如果您不熟悉Executors框架,請閱讀Thread Pool Executor 。
有關偵聽器的更多詳細信息,請閱讀Servlet Listener Tutorial 。
工作線程實現
AsyncRequestProcessor.java
package com.journaldev.servlet.async;import java.io.IOException; import java.io.PrintWriter;import javax.servlet.AsyncContext;public class AsyncRequestProcessor implements Runnable {private AsyncContext asyncContext;private int secs;public AsyncRequestProcessor() {}public AsyncRequestProcessor(AsyncContext asyncCtx, int secs) {this.asyncContext = asyncCtx;this.secs = secs;}@Overridepublic void run() {System.out.println("Async Supported? "+ asyncContext.getRequest().isAsyncSupported());longProcessing(secs);try {PrintWriter out = asyncContext.getResponse().getWriter();out.write("Processing done for " + secs + " milliseconds!!");} catch (IOException e) {e.printStackTrace();}//complete the processingasyncContext.complete();}private void longProcessing(int secs) {// wait for given time before finishingtry {Thread.sleep(secs);} catch (InterruptedException e) {e.printStackTrace();}} }請注意,AsyncContext的用法及其在獲取請求和響應對象,然后通過complete()方法調用完成異步處理的用法。
AsyncListener實現
AppAsyncListener.java
package com.journaldev.servlet.async;import java.io.IOException; import java.io.PrintWriter;import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebListener;@WebListener public class AppAsyncListener implements AsyncListener {@Overridepublic void onComplete(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onComplete");// we can do resource cleanup activity here}@Overridepublic void onError(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onError");//we can return error response to client}@Overridepublic void onStartAsync(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onStartAsync");//we can log the event here}@Overridepublic void onTimeout(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onTimeout");//we can send appropriate response to clientServletResponse response = asyncEvent.getAsyncContext().getResponse();PrintWriter out = response.getWriter();out.write("TimeOut Error in Processing");}} 注意onTimeout()方法的實現,在該方法中,我們向客戶端發送超時響應。 
這是我們異步servlet的實現,請注意使用AsyncContext和ThreadPoolExecutor進行處理。
AsyncLongRunningServlet.java
package com.journaldev.servlet.async;import java.io.IOException; import java.util.concurrent.ThreadPoolExecutor;import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;@WebServlet(urlPatterns = "/AsyncLongRunningServlet", asyncSupported = true) public class AsyncLongRunningServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {long startTime = System.currentTimeMillis();System.out.println("AsyncLongRunningServlet Start::Name="+ Thread.currentThread().getName() + "::ID="+ Thread.currentThread().getId());request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);String time = request.getParameter("time");int secs = Integer.valueOf(time);// max 10 secondsif (secs > 10000)secs = 10000;AsyncContext asyncCtx = request.startAsync();asyncCtx.addListener(new AppAsyncListener());asyncCtx.setTimeout(9000);ThreadPoolExecutor executor = (ThreadPoolExecutor) request.getServletContext().getAttribute("executor");executor.execute(new AsyncRequestProcessor(asyncCtx, secs));long endTime = System.currentTimeMillis();System.out.println("AsyncLongRunningServlet End::Name="+ Thread.currentThread().getName() + "::ID="+ Thread.currentThread().getId() + "::Time Taken="+ (endTime - startTime) + " ms.");}}運行異步Servlet
現在,當我們在servlet上運行,URL為http://localhost:8080/AsyncServletExample/AsyncLongRunningServlet?time=8000我們得到的響應和日志如下:
AsyncLongRunningServlet Start::Name=http-bio-8080-exec-50::ID=124 AsyncLongRunningServlet End::Name=http-bio-8080-exec-50::ID=124::Time Taken=1 ms. Async Supported? true AppAsyncListener onComplete如果我們將時間設置為9999,則會發生超時,并在客戶端以“處理中的超時錯誤”和日志形式獲得響應:
AsyncLongRunningServlet Start::Name=http-bio-8080-exec-44::ID=117 AsyncLongRunningServlet End::Name=http-bio-8080-exec-44::ID=117::Time Taken=1 ms. Async Supported? true AppAsyncListener onTimeout AppAsyncListener onError AppAsyncListener onComplete Exception in thread "pool-5-thread-6" java.lang.IllegalStateException: The request associated with the AsyncContext has already completed processing.at org.apache.catalina.core.AsyncContextImpl.check(AsyncContextImpl.java:439)at org.apache.catalina.core.AsyncContextImpl.getResponse(AsyncContextImpl.java:197)at com.journaldev.servlet.async.AsyncRequestProcessor.run(AsyncRequestProcessor.java:27)at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)at java.lang.Thread.run(Thread.java:680)注意,servlet線程快速完成了執行,所有主要的處理工作都在其他線程中進行。
這就是異步Servlet的全部,希望您喜歡它。
- 下載AsyncServletExample項目
翻譯自: https://www.javacodegeeks.com/2013/08/async-servlet-feature-of-servlet-3.html
總結
以上是生活随笔為你收集整理的Servlet 3的异步Servlet功能的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 宝马推出 i5 eDrive40 和 M
- 下一篇: 云闪付转账转账记录怎么查?
