daily 2017 9 11 spring mvc的异步处理 - wtdig/study GitHub Wiki
使用场景: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可以注册一个请求完成的回调函数。