springmvc基于servlet3.0特性记录

转自 http://13shu.iteye.com/blog/2021652



servlet3.0有一个新的特性,新增HTTP请求的异步处理,详细请参考

由于项目采用的SpringMVC做的,所以查看了下SpringMVC的资料,发现3.2版本对于异步处理有良好的封装。 

  • 配置servlet3.0

     web.xml中配置

    

 

Xml代码   收藏代码
  1. <web-app version="3.0"   
  2.     xmlns="http://java.sun.com/xml/ns/javaee"   
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
  5.     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">  
 

 

Xml代码   收藏代码
  1. <servlet>  
  2.         <servlet-name>test</servlet-name>  
  3.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  4.         <load-on-startup>2</load-on-startup>  
  5.         <strong><async-supported>true</async-supported></strong>  
  6.     </servlet>  
  7.   
  8.     <servlet-mapping>  
  9.         <servlet-name>test</servlet-name>  
  10.         <url-pattern>/</url-pattern>  
  11.     <servlet-mapping>  

 

  • Spring控制层代码修改返回类型为Callable模型
Java代码   收藏代码
  1. public Callable<String> processUpload(HttpServletRequest request,  
  2.             final HttpServletResponse response) {  
  3.         System.out.println("线程名称:"+Thread.currentThread().getName());  
  4.         return new Callable<String>() {  
  5.             public String call() throws Exception {  
  6.                 try {  
  7.                     System.out.println("线程名称:"+Thread.currentThread().getName());  
  8.                     response.setContentType("text/plain;charset=utf-8");  
  9.                     response.getWriter().write("nihao");  
  10.                     response.getWriter().close();  
  11.                 } catch (IOException e) {  
  12.                     e.printStackTrace();  
  13.                 }  
  14.                 return null;  
  15.             }  
  16.         };  
  17.     }  

控制台输出内容:

13:46:59,875 INFO  [stdout] (http-localhost-127.0.0.1-8080-2) 线程名称:http-localhost-127.0.0.1-8080-2

13:48:57,609 INFO  [stdout] (abc-2) 线程名称:abc-2

Callable内部与外部输出了当前线程的名字,发现线程被切换了,request请求的线程被释放到了web容器中(如:tomcat)。

 

问题貌似解决了酷,离下班时间还早于是乎想看下这个神奇的过程是怎么发生的,看了下Spring的源代码,不看不知道,一看吓一跳。

 

Spring返回的Callable被RequestMappingHandlerAdapter拦截处理了,结果发现Callable被SimpleAsyncTaskExecutor线程池处理了,经过细心的查看代码发现SimpleAsyncTaskExecutor,每当任务被提交到此“线程池(这里就交线程池了)”时,线程池产生一个新的线程去执行Callable中的代码,每次都产生新的线程而且没有上线(默认没有上线,可以设置concurrencyLimit属性来设置线程数的大小)。

 

Java代码   收藏代码
  1. public void execute(Runnable task, long startTimeout) {  
  2.         Assert.notNull(task, "Runnable must not be null");  
  3.         if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {  
  4.             this.concurrencyThrottle.beforeAccess();  
  5.             doExecute(new ConcurrencyThrottlingRunnable(task));  
  6.         }  
  7.         else {  
  8.             doExecute(task);  
  9.         }  
  10.     }  
 执行任务前会先判断有没有空余的线程空间(concurrencyLimit默认为-1,不执行)

 

 

Java代码   收藏代码
  1. protected void beforeAccess() {  
  2.         if (this.concurrencyLimit == NO_CONCURRENCY) {  
  3.             throw new IllegalStateException(  
  4.                     "Currently no invocations allowed - concurrency limit set to NO_CONCURRENCY");  
  5.         }  
  6.         if (this.concurrencyLimit > 0) {  
  7.             boolean debug = logger.isDebugEnabled();  
  8.             synchronized (this.monitor) {  
  9.                 boolean interrupted = false;  
  10.                 while (this.concurrencyCount >= this.concurrencyLimit) {  
  11.                     if (interrupted) {  
  12.                         throw new IllegalStateException("Thread was interrupted while waiting for invocation access, " +  
  13.                                 "but concurrency limit still does not allow for entering");  
  14.                     }  
  15.                     if (debug) {  
  16.                         logger.debug("Concurrency count " + this.concurrencyCount +  
  17.                                 " has reached limit " + this.concurrencyLimit + " - blocking");  
  18.                     }  
  19.                     try {  
  20.                         this.monitor.wait();  
  21.                     }  
  22.                     catch (InterruptedException ex) {  
  23.                         // Re-interrupt current thread, to allow other threads to react.  
  24.                         Thread.currentThread().interrupt();  
  25.                         interrupted = true;  
  26.                     }  
  27.                 }  
  28.                 if (debug) {  
  29.                     logger.debug("Entering throttle at concurrency count " + this.concurrencyCount);  
  30.                 }  
  31.                 this.concurrencyCount++;  
  32.             }  
  33.         }  
  34.     }  
 如果没有超过concurrencyLimit,就不会this.monitor.wait();产生新的线程执行任务。

 

Spring中应该有其他线程池来支持此功能。最终选择ThreadPoolTaskExecutor,看起来与ThreadPoolExecutor类似。

 

Java代码   收藏代码
  1.        private int corePoolSize = 1;  
  2.   
  3. private int maxPoolSize = Integer.MAX_VALUE;  
  4.   
  5. private int keepAliveSeconds = 60;  
  6.   
  7. private boolean allowCoreThreadTimeOut = false;  
  8.   
  9. private int queueCapacity = Integer.MAX_VALUE;  
  10.   
  11. private ThreadPoolExecutor threadPoolExecutor;  
 具备线程池的功能,可以设置线程的大小,有缓冲队列,空闲线程存活时间等。

 

 默认corePoolSize=1,maxPoolSize、queueCapacity为整型最大值,keepAliveSeconds=60(单位秒)

Xml代码   收藏代码
  1. <bean id="myThreadPool"  
  2.         class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">  
  3.         <property name="corePoolSize" value="5" /><!--最小线程数 -->  
  4.         <property name="maxPoolSize" value="10" /><!--最大线程数 -->  
  5.         <property name="queueCapacity" value="50" /><!--缓冲队列大小 -->  
  6.         <property name="threadNamePrefix" value="abc-" /><!--线程池中产生的线程名字前缀 -->  
  7.         <property name="keepAliveSeconds" value="30" /><!--线程池中空闲线程的存活时间单位秒 -->  
  8.     </bean>  
  9.   
  10.     <mvc:annotation-driven>  
  11.         <mvc:async-support task-executor="myThreadPool"  
  12.             default-timeout="600">  
  13.             <mvc:callable-interceptors>  
  14.                 <bean class="com.zhongyu.ABC" />  
  15.             </mvc:callable-interceptors>  
  16.         </mvc:async-support>  
  17.     </mvc:annotation-driven>  

采用annotation-driven方式配置线程池,异步操作拦截器

Java代码   收藏代码
  1. public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task)  
  2.         throws Exception {  
  3.     HttpServletResponse servletResponse = request.getNativeResponse(HttpServletResponse.class);  
  4.     if (!servletResponse.isCommitted()) {  
  5.         servletResponse.setContentType("text/plain;charset=utf-8");  
  6.         servletResponse.getWriter().write("超时了");  
  7.         servletResponse.getWriter().close();  
  8.     }  
  9.     return null;  
  10. }  

 请求设定default-timeout超时后,会调用拦截器中handleTimeout进行逻辑处理

还需要优化的地方:

  • 由于ThreadPoolTaskExecutor内部缓冲队列采用的是阻塞队列LinkedBlockingDeque,如果队列满了,外部线程会继续等待,需要设置HTTP请求的超时时间
  • 线程池的大小配置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值