daily 2017 9 11 spring mvc的异步处理 - wtdig/study GitHub Wiki

spring mvc的异步处理

使用场景:1.web服务器能创建的最大线程数往往是有限制的,如果当前访问量比较大,而处理逻辑比较耗时,这时可用的web线程数就会减少,此时把处理交给其他非web线程来处理,就可以腾出更多可用的web线程供其他访问使用,也就是说可以增大吞掉量。2.还可以方便的模拟长连接,如long polling .

long polling解释:Long Polling原理其实很简单,也很讨巧。与Polling相比,Long Polling客户端也许不会马上收到来自服务端的响应,需要等待一些时间(直到有新消息,或者连接timeout了等等)。同样的,客户端也不再需要定时向服务发送请求了,而是直到收到服务端响应之后,或者连接丢失之后,客户端接着马上请求客户端。

这里,我打个比方,传统的Polling一般是由C向S询问:“有我的信件吗?”。S接到询问之后,会立即查询,并且把查询结果告诉C,不管有没有C的信件,要么回复:“嗯,你有X封信。”,要么回复:“没,没有你的信”。

而Long Polling更像是这样,C向S发出询问:“有我的信件吗?”,S开始查询,如果有则回复C:“嗯,有你x封信”。如果没有,则不作任何回复,而是让C等着,自己一遍一遍地查询是否有订阅者的信。

换句话说:当S收到C的查询请求之后,Polling则只查询一次,并且把查询结果告诉C;而Long Polling收到请求之后,则会一遍一遍地查询,直到有消息才会响应C,不然一直hold Client。

springmvc 3.2开始就支持servlet3.0的异步请求。平常我们请求一个controller一般都是同步的,如果在代码执行中,遇到耗时的业务操作,那servlet容器线程就会被锁死,当有其他请求进来的时候就会受堵了。

springmvc3.2之后支持异步请求,能够在controller中返回一个Callable或者DeferredResult。当返回Callable的时候,大概的执行过程如下:

当controller返回值是Callable的时候,springmvc就会启动一个线程将Callable交给TaskExecutor去处理

然后DispatcherServlet还有所有的spring拦截器都退出主线程,然后把response保持打开的状态

当Callable执行结束之后,springmvc就会重新启动分配一个request请求,然后DispatcherServlet就重新调用和处理Callable异步执行的返回结果,然后返回视图

DeferredResult的执行过程和Callable差不多,唯一不同的时候,DeferredResult是由应用程序其他线程执行返回结果,而Callable是由

TaskExecutor执行返回结果。

使用步骤:

1.需要在web.xml加上servlet3.0的scheme库,http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"

2、在web.xml的servlet还有filter添加true子节点,注意servlet和filter都需要加上,否则报错

3、然后就可以在controller中执行异步请求了,利用Callable执行异步请求,并返回视图:代码如下:

@RequestMapping("/mvc25")

public Callable<String> mvc25() {
return new Callable<String>() {
    @Override
    public String call() throws Exception {
         Thread.sleep(2000);
         return "task/task";
    }
};

}

4、可以自定义客户端超时间

@RequestMapping("/mvc27")

@ResponseBody

public WebAsyncTask<String> mvc27() {
Callable<String> callable = new Callable<String>() {
    @Override
    public String call() throws Exception {
        Thread.sleep(10000);
        return "hello task";
    }
};

return new WebAsyncTask<String>(10000, callable);

}

5、异常的处理:如果在线程的执行过程中,遇到异常,处理过程和普通请求的一样,你可以用@ExceptionHandler来处理或者定义全局的HandlerExceptionResolver来处理

@RequestMapping("/mvc28")

@ResponseBody

public Callable<String> mvc28() {
Callable<String> callable = new Callable<String>() {
    @Override
    public String call() throws Exception {
        Thread.sleep(2000);
        throw new RuntimeException();
    }
};

return callable;

}

@ExceptionHandler(RuntimeException.class)

@ResponseBody

public JSONObject handlerException(){
JSONObject jsonObject = new JSONObject();
jsonObject.put("aaa", 123);

return jsonObject ;

}

6、还可以通过返回DeferredResult返回,DeferredResult的作用是返回一个实例给其他线程来处理这个异步请求。

@RequestMapping("/mvc29")

@ResponseBody

public DeferredResult<String> mvc29() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
dealInOtherThread(deferredResult);
return deferredResult;

}

private void dealInOtherThread(DeferredResult deferredResult) { try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }
deferredResult.setResult("hello task");

}

dealInOtherThread处理完成,setResult的时候就会触发springmvc分配一个request到DispatcherServlet,然后DispatcherServlet处理DeferredResult的返回结果,并返回视图。

DeferredResult还提供了其他返回来处理线程请求,例如onTimeout(Runnable) 还有onCompletion(Runnable),onTimeout可以注册一个线程回调,当请求延时的时候的回调函数,onCompletion可以注册一个请求完成的回调函数。

⚠️ **GitHub.com Fallback** ⚠️