springmvc callable处理异步请求


使用业务场景: 对于有的请求业务处理流程可能比较耗时,比如长查询,远程调用等,主线程会被一直占用,而tomcat线程池线程有限,处理量就会下降

servlet3.0以后提供了对异步处理的支持,springmvc封装了异步处理,满足用户请求后,主线程很快结束,并开启其它线程处理任务,并将处理结果响应用户,而主线程就可以接收更多请求。

参考官方解释:

https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support

原理简介:对于一次请求,比如front/test

1,springmvc开启副线程处理业务(将Callable 提交到 TaskExecutor)

2,DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态

3,Callable返回结果,SpringMVC将请求front/test重新派发给容器(再重新请求一次front/test),恢复之前的处理;

4,DispatcherServlet重新被调用,将结果返回给用户

使用条件:mvc4.0以上,servlet3.0以上

使用示例

 1 import java.util.concurrent.Callable;
 2 import java.util.concurrent.TimeUnit; 3 4 import org.slf4j.Logger; 5 import org.slf4j.LoggerFactory; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 import org.springframework.web.bind.annotation.RestController; 8 @RestController 9 public class RestfulAsync { 10 private Logger logger = LoggerFactory.getLogger(getClass()); 11 @RequestMapping("/front/test") 12 public Callable<String> order() { 13 logger.info("主线程开始"); 14 Callable<String> result = () -> { 15 logger.info("副线程开始"); 16 TimeUnit.SECONDS.sleep(1); 17 logger.info("副线程返回"); 18 return "success"; 19  }; 20 logger.info("主线程返回"); 21 return result; 22  } 23 }

配置文件:
1,web.xml 3.0以上

<!--mvc配置-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<!--在此处增加异步支持-->
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
注意所有的过滤器filter也必须增加异步支持<async-supported>true</async-supported>如:
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2,spring-mv.xml配置
注意必须指定所需的命名空间
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- spring mvc 异步处理基于任务线程池 -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10" /><!--核心线程数 --> 
<property name="maxPoolSize" value="100" /><!--最大线程数 --> 
<property name="queueCapacity" value="1024" /><!--缓冲队列大小 --> 
<property name="threadNamePrefix" value="http-do-business-pool-" /><!--线程池中产生的线程名字前缀 --> 
<property name="keepAliveSeconds" value="30" /><!--线程池中空闲线程的存活时间单位秒 -->
</bean>
<mvc:annotation-driven>
<mvc:async-support task-executor="taskExecutor" //此处指定自定义线程池,否则异步处理会使用默认线程池SimpleAsyncTaskExecutor,这个线程大小无限制,会有问题
// 会提示:An Executor is required to handle java.util.concurrent.Callable return values.Please, configure a TaskExecutor in the MVC config under "async support".The SimpleAsyncTaskExecutor currently in use is not suitable under load
default-timeout="1000"> //超时时间,单位毫秒
<mvc:callable-interceptors> 
<bean class="com.ronglian.bms.commons.interceptor.MyCallableInterceptor" /> // 指定异步处理超时拦截器 
</mvc:callable-interceptors> 
</mvc:async-support>
</mvc:annotation-driven>

3,异步处理超时拦截器示例:

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;

import com.google.gson.Gson;
import com.ronglian.bms.restful.ResultEnum;
/**
* 异步处理超时拦截器,处理业务超时的情况,xml配置见
* <mvc:annotation-driven>
<mvc:async-support task-executor="taskExecutor" 
default-timeout="1000"> 
<mvc:callable-interceptors> 
<bean class="com.ronglian.bms.commons.interceptor.MyCallableInterceptor" /> 
</mvc:callable-interceptors> 
</mvc:async-support>
</mvc:annotation-driven>
* @author zli
* @Date 2019-03-06
*/
public class MyCallableInterceptor implements CallableProcessingInterceptor{
public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
HttpServletResponse servletResponse = request.getNativeResponse(HttpServletResponse.class);
if (!servletResponse.isCommitted()) {
//return "service time out";
servletResponse.setContentType("application/json;charset=utf-8");
String obj = this.buildResult(ResultEnum.ERROR_TIME_OUT.getCode(), ResultEnum.ERROR_TIME_OUT.getMsg(), null);
servletResponse.getWriter().write(obj);
servletResponse.getWriter().close();
}
return null;
}
private String buildResult(String headerCode, String headMsg, Object bodyResult) {
    Map<String, Object> resultMap = new HashMap<String, Object>();
    Map<String, String> headMap = new HashMap<String, String>();
    headMap.put("retCode", headerCode);
    headMap.put("retMsg", headMsg);
    resultMap.put("header", headMap);
    resultMap.put("body", bodyResult);
    Gson g = new Gson();
    return g.toJson(resultMap);
}
}

  另外一种带超时处理的Callable,即WebAsyncTask,示例代码如下

      

/**
     * WebAsyncTask示例,带处理超时的Callable
     * @return
     */
    @RequestMapping("/front/testwithtimeout")
    public WebAsyncTask<String> test2() {
        logger.info("主线程开始");
        Callable<String> result = () -> {
            logger.info("副线程开始");
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (Exception e) {
                // TODO: handle exception
            }
            logger.info("副线程返回");
            return "success";
        };
        logger.info("主线程返回");
        WebAsyncTask<String> wat = new WebAsyncTask<String>(3000L, result);
        wat.onTimeout(new Callable<String>() {
            
            @Override
            public String call() throws Exception {
                // TODO Auto-generated method stub
                return "超时";
            }
        });
        return wat;
    }


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM