日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Servlet 3的异步Servlet功能

發布時間:2023/12/3 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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應該具有@WebServlet 批注 ,其asyncSupported值為true
  • 由于實際工作將委托給另一個線程,因此我們應該有一個線程池實現。 我們可以使用Executors框架創建線程池,并使用servlet上下文偵聽器啟動線程池。
  • 我們需要通過ServletRequest.startAsync()方法獲取AsyncContext的實例。 AsyncContext提供了獲取ServletRequest和ServletResponse對象引用的方法。 它還提供了使用dispatch()方法將請求轉發到另一個資源的方法。
  • 我們應該有一個Runnable實現 ,在這里我們將進行繁重的處理,然后使用AsyncContext對象將請求分派到另一個資源,或者使用ServletResponse對象寫入響應。 處理完成后,我們應調用AsyncContext.complete()方法以使容器知道異步處理已完成。
  • 我們可以將AsyncListener實現添加到AsyncContext對象中以實現回調方法–我們可以使用它為異步線程處理過程中出現錯誤或超時的情況提供對客戶端的錯誤響應。 我們還可以在此處進行一些清理活動。
  • 一旦我們完成了異步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項目

    參考: Developer Recipes博客上來自JCG合作伙伴 Pankaj Kumar 的Servlet 3異步Servlet功能 。

    翻譯自: https://www.javacodegeeks.com/2013/08/async-servlet-feature-of-servlet-3.html

    總結

    以上是生活随笔為你收集整理的Servlet 3的异步Servlet功能的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。