自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Servlet3中異步Servlet特性介紹

譯文
開(kāi)發(fā) 前端
在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)。在本文中,將以實(shí)際的例子來(lái)講解下Servlet 3規(guī)范中對(duì)異步操作的支持。

在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)。

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

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

下面我們開(kāi)始了解下,如果不使用異步特性的一個(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.         //如果超過(guò)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.

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

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

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

  下面我們通過(guò)例子來(lái)說(shuō)明異步Servlet的實(shí)現(xiàn)方法:

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

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

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

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

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

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

 

 

#p#

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

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

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

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

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

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

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

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

#p#

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

下面運(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è)線程中處理的,不存在阻塞問(wèn)題。

 原文鏈接:http://www.javacodegeeks.com/2013/08/async-servlet-feature-of-servlet-3.html

責(zé)任編輯:陳四芳 來(lái)源: 51CTO
相關(guān)推薦

2009-07-08 09:35:53

Java ServleServlet 3.0

2009-07-09 11:27:59

Servlet容器

2009-07-07 10:08:49

Future Resp

2009-07-09 13:39:52

Servlet Con

2024-12-10 00:00:30

ServletTomcat異步

2009-07-09 13:04:37

Servlet接口

2009-07-09 10:49:56

Servlet和JSP

2009-07-07 09:41:02

異步ServletAJAX

2010-01-14 09:15:07

Java EE 6Servlet 3.0異步處理

2021-06-02 10:39:59

ServletWebFluxSpringMVC

2009-07-08 11:17:10

Servlet容器Servlet Con

2009-07-09 13:23:44

Servlet 2.4

2009-07-07 15:24:49

Http Servle

2009-07-07 09:51:49

Servlet實(shí)例

2009-07-03 11:21:43

Servlet和JSPJSP路徑

2009-07-07 16:05:15

Servlet和Jav

2009-07-07 17:32:31

HTTP Servle

2010-04-30 09:19:05

Servlet 3.0

2009-07-08 15:59:55

doFilter方法

2009-07-08 14:01:47

Servlet容器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)