日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Servlet3中異步Servlet特性介紹

在Jave EE 6規(guī)范中,關(guān)于Servlet 3規(guī)范的相關(guān)功能增強(qiáng),一直是讓大部分用戶忽略的,連直到最新的Spring MVC 3.2才支持Servlet 3的異步調(diào)用。這可能跟大部分用戶使用的JAVE EE容器依然是舊的有關(guān)系(如支持Servlet 3規(guī)范的需要Tomcat 7,但目前不少用戶還在使用Tomcat 6)。

成都創(chuàng)新互聯(lián)公司專注于平?jīng)銎髽I(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站建設(shè),電子商務(wù)商城網(wǎng)站建設(shè)。平?jīng)鼍W(wǎng)站建設(shè)公司,為平?jīng)龅鹊貐^(qū)提供建站服務(wù)。全流程定制開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)

在本文中,將以實(shí)際的例子來講解下Servlet 3規(guī)范中對異步操作的支持。

首先要簡單了解,在Servlet 3中,已經(jīng)支持使用注解的方式去進(jìn)行Servlet的配置,這樣就不需要在web.xml中進(jìn)行傳統(tǒng)的xml的配置了,最常用的注解是使用@WebServlet、@WebFilter、@WebInitParam,它們分別等價(jià)于傳統(tǒng)xml配置中的、,其他參數(shù)可參考Servlet 3中的規(guī)范說明。

下面我們開始了解下,如果不使用異步特性的一個(gè)例子,代碼如下:

 
 
 
 
  1. @WebServlet("/LongRunningServlet") 
  2. public class LongRunningServlet extends HttpServlet { 
  3.     private static final long serialVersionUID = 1L; 
  4.  
  5.     protected void doGet(HttpServletRequest request, 
  6.             HttpServletResponse response) throws ServletException, IOException { 
  7.         long startTime = System.currentTimeMillis(); 
  8.         System.out.println("LongRunningServlet Start::Name=" 
  9.                 + Thread.currentThread().getName() + "::ID=" 
  10.                 + Thread.currentThread().getId()); 
  11.  
  12.         String time = request.getParameter("time"); 
  13.         int secs = Integer.valueOf(time); 
  14.         //如果超過10秒,默認(rèn)用10秒 
  15.         if (secs > 10000) 
  16.             secs = 10000; 
  17.  
  18.         longProcessing(secs); 
  19.  
  20.         PrintWriter out = response.getWriter(); 
  21.         long endTime = System.currentTimeMillis(); 
  22.         out.write("Processing done for " + secs + " milliseconds!!"); 
  23.         System.out.println("LongRunningServlet Start::Name=" 
  24.                 + Thread.currentThread().getName() + "::ID=" 
  25.                 + Thread.currentThread().getId() + "::Time Taken=" 
  26.                 + (endTime - startTime) + " ms."); 
  27.     } 
  28.  
  29.     private void longProcessing(int secs) { 
  30.               //故意讓線程睡眠      
  31.         try { 
  32.             Thread.sleep(secs); 
  33.         } catch (InterruptedException e) { 
  34.             e.printStackTrace(); 
  35.         } 
  36.     } 
  37.  

 運(yùn)行上面的例子,輸入

http://localhost:8080/AsyncServletExample/LongRunningServlet?time=8000,則可以看到輸出為:

LongRunningServlet Start::Name=http-bio-8080-exec-34::ID=103

1. LongRunningServlet Start::Name=http-bio-8080-exec-34::ID=103::Time Taken=8002 ms.

  可以觀察到,在主線程啟動后,servlet線程為了處理longProcessing的請求,足足等待了8秒,最后才輸出結(jié)果進(jìn)行響應(yīng),這樣對于高并發(fā)的應(yīng)用來說這是很大的瓶頸,因?yàn)楸仨氁降鹊酱幚淼姆椒ㄍ瓿珊螅琒ervlet容器中的線程才能繼續(xù)接收其他請求,在此之前,Servlet線程一直處于阻塞狀態(tài)。

  在Servlet 3.0規(guī)范前,是有一些相關(guān)的解決方案的,比如常見的就是使用一個(gè)單獨(dú)的工作線程(worker thread)去處理這些耗費(fèi)時(shí)間的工作,而Servlet 容器中的線程在把工作交給工作線程處理后則馬上回收到Servlet容器中去。比如Tomcat的Comet、WebLogic的的FutureResponseServlet和WebSphere的Asynchronous Request Dispatcher都是這類型的解決方案。

   但只這些方案的弊端是沒辦法很容易地在不修改代碼的情況下遷移到其他Servlet容器中,這就是Servlet 3中要定義異步Servlet的原因所在。

  下面我們通過例子來說明異步Servlet的實(shí)現(xiàn)方法:

1、 首先設(shè)置servlet要支持異步屬性,這個(gè)只需要設(shè)置asyncSupported屬性為true就可以了。

2、 因?yàn)閷?shí)際上的工作是委托給另外的線程的,我們應(yīng)該實(shí)現(xiàn)一個(gè)線程池,這個(gè)可以通過使用Executors框架去實(shí)現(xiàn)(具體參考http://www.journaldev.com/1069/java-thread-pool-example-using-executors-and-threadpoolexecutor一文),并且使用Servlet Context listener去初始化線程池。

3、 我們需要通過ServletRequest.startAsync()方法獲得AsyncContext的實(shí)例。AsyncContext提供了方法去獲得ServletRequest和ServletResponse的對象引用。它也能使用dispatch()方法去將請求forward到其他資源。

4、 我們將實(shí)現(xiàn)Runnable接口,并且在其實(shí)現(xiàn)方法中處理各種耗時(shí)的任務(wù),然后使用AsyncContext對象去將請求dispatch到其他資源中去或者使用ServletResponse對象輸出。一旦處理完畢,將調(diào)用AsyncContext.complete()方法去讓容器知道異步處理已經(jīng)結(jié)束。

5、 我們還可以在AsyncContext對象增加AsyncListener的實(shí)現(xiàn)類以實(shí)現(xiàn)相關(guān)的徽調(diào)方法,可以使用這個(gè)去提供將錯(cuò)誤信息返回給用戶(如超時(shí)或其他出錯(cuò)信息),也可以做一些資源清理的工作。

  我們來看下完成后例子的工程結(jié)構(gòu)圖如下:

#p#

下面我們看下實(shí)現(xiàn)了ServletContextListener類的監(jiān)聽類代碼:

 
 
 
 
  1.   AppContextListener.java
  2. package com.journaldev.servlet.async;
  3. import java.util.concurrent.ArrayBlockingQueue;
  4. import java.util.concurrent.ThreadPoolExecutor;
  5. import java.util.concurrent.TimeUnit;
  6. import javax.servlet.ServletContextEvent;
  7. import javax.servlet.ServletContextListener;
  8. import javax.servlet.annotation.WebListener;
  9. @WebListener
  10. public class AppContextListener implements ServletContextListener {
  11. public void contextInitialized(ServletContextEvent servletContextEvent) {
  12. // 創(chuàng)建線程池
  13. ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L,
  14. TimeUnit.MILLISECONDS, new ArrayBlockingQueue(100));
  15. servletContextEvent.getServletContext().setAttribute("executor",
  16. executor);
  17. }
  18. public void contextDestroyed(ServletContextEvent servletContextEvent) {
  19. ThreadPoolExecutor executor = (ThreadPoolExecutor) servletContextEvent
  20. .getServletContext().getAttribute("executor");
  21. executor.shutdown();
  22. }
  23. }

然后是worker線程的實(shí)現(xiàn)代碼,如下:

 
 
 
 
  1. AsyncRequestProcessor.java
  2. package com.journaldev.servlet.async;
  3. import java.io.IOException;
  4. import java.io.PrintWriter;
  5. import javax.servlet.AsyncContext;
  6. public class AsyncRequestProcessor implements Runnable {
  7.     private AsyncContext asyncContext;
  8.     private int secs;
  9.     public AsyncRequestProcessor() {
  10.     }
  11.     public AsyncRequestProcessor(AsyncContext asyncCtx, int secs) {
  12.         this.asyncContext = asyncCtx;
  13.         this.secs = secs;
  14.     }
  15.     @Override
  16.     public void run() {
  17.         System.out.println("Async Supported? "
  18.                 + asyncContext.getRequest().isAsyncSupported());
  19.         longProcessing(secs);
  20.         try {
  21.             PrintWriter out = asyncContext.getResponse().getWriter();
  22.             out.write("Processing done for " + secs + " milliseconds!!");
  23.         } catch (IOException e) {
  24.             e.printStackTrace();
  25.         }
  26.         //完成異步線程處理
  27.         asyncContext.complete();
  28.     }
  29.     private void longProcessing(int secs) {
  30.         // 休眠指定的時(shí)間
  31.         try {
  32.             Thread.sleep(secs);
  33.         } catch (InterruptedException e) {
  34.             e.printStackTrace();
  35.         }
  36.     }
  37. }

請?jiān)谶@里注意AsyncContext的使用方法,以及當(dāng)完成異步調(diào)用時(shí)必須調(diào)用asyncContext.complete()方法。

現(xiàn)在看下AsyncListener類的實(shí)現(xiàn)

 
 
 
 
  1. package com.journaldev.servlet.async;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import javax.servlet.AsyncEvent;
  5. import javax.servlet.AsyncListener;
  6. import javax.servlet.ServletResponse;
  7. import javax.servlet.annotation.WebListener;
  8. @WebListener
  9. public class AppAsyncListener implements AsyncListener {
  10.     @Override
  11.     public void onComplete(AsyncEvent asyncEvent) throws IOException {
  12.         System.out.println("AppAsyncListener onComplete");
  13.         // 在這里可以做一些資源清理工作
  14.     }
  15.     @Override
  16.     public void onError(AsyncEvent asyncEvent) throws IOException {
  17.         System.out.println("AppAsyncListener onError");
  18.         //這里可以拋出錯(cuò)誤信息
  19.     }
  20.     @Override
  21.     public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
  22.         System.out.println("AppAsyncListener onStartAsync");
  23.         //可以記錄相關(guān)日志
  24.     }
  25.     @Override
  26.     public void onTimeout(AsyncEvent asyncEvent) throws IOException {
  27.         System.out.println("AppAsyncListener onTimeout");
  28.         ServletResponse response = asyncEvent.getAsyncContext().getResponse();
  29.         PrintWriter out = response.getWriter();
  30.         out.write("TimeOut Error in Processing");
  31.     }
  32. } 

其中請注意可以監(jiān)聽onTimeout事件的使用,可以有效地返回給用戶端出錯(cuò)的信息。最后來重新改寫下前文提到的測試Servlet的代碼如下:

#p#

 
 
 
 
  1. AsyncLongRunningServlet.java
  2. package com.journaldev.servlet.async;
  3. import java.io.IOException;
  4. import java.util.concurrent.ThreadPoolExecutor;
  5. import javax.servlet.AsyncContext;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.annotation.WebServlet;
  8. import javax.servlet.http.HttpServlet;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletResponse;
  11. @WebServlet(urlPatterns = "/AsyncLongRunningServlet", asyncSupported = true)
  12. public class AsyncLongRunningServlet extends HttpServlet {
  13.     private static final long serialVersionUID = 1L;
  14.     protected void doGet(HttpServletRequest request,
  15.             HttpServletResponse response) throws ServletException, IOException {
  16.         long startTime = System.currentTimeMillis();
  17.         System.out.println("AsyncLongRunningServlet Start::Name="
  18.                 + Thread.currentThread().getName() + "::ID="
  19.                 + Thread.currentThread().getId());
  20.         request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
  21.         String time = request.getParameter("time");
  22.         int secs = Integer.valueOf(time);
  23.         // 如果超過10秒則設(shè)置為10秒
  24.         if (secs > 10000)
  25.             secs = 10000;
  26.         AsyncContext asyncCtx = request.startAsync();
  27.         asyncCtx.addListener(new AppAsyncListener());
  28.         asyncCtx.setTimeout(9000);
  29.         ThreadPoolExecutor executor = (ThreadPoolExecutor) request
  30.                 .getServletContext().getAttribute("executor");
  31.         executor.execute(new AsyncRequestProcessor(asyncCtx, secs));
  32.         long endTime = System.currentTimeMillis();
  33.         System.out.println("AsyncLongRunningServlet End::Name="
  34.                 + Thread.currentThread().getName() + "::ID="
  35.                 + Thread.currentThread().getId() + "::Time Taken="
  36.                 + (endTime - startTime) + " ms.");
  37.     }
  38. }

下面運(yùn)行這個(gè)Servlet程序,輸入:

http://localhost:8080/AsyncServletExample/AsyncLongRunningServlet?time=8000,運(yùn)行結(jié)果為:

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

但如果我們運(yùn)行一個(gè)time=9999的輸入,則運(yùn)行結(jié)果為:

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主線程很快執(zhí)行完畢并且所有的處理額外的工作都是在另外一個(gè)線程中處理的,不存在阻塞問題。


分享名稱:Servlet3中異步Servlet特性介紹
標(biāo)題來源:http://m.5511xx.com/article/codohgs.html