1)Spring 异步请求使用示例:
@RequestMapping(value ="/{id}",method = RequestMethod.GET)
public Callable<Contents> getContentById(@PathVariable("id") Integer id) throws Exception {
Contents content = new Contents();
content.setDetail("测试内容");
return ()-> content;
}
大家可以思考一下,接口返回的对象用 callable包一层,有什么优势?
这种接口规范是之前的架构师要求的,当时不是很明白。后来跟着源码过了一遍,原理清晰起来。
还是先上流程图,然后分析源码。
下面直接看源码。
2)源码分析:
首先是请求入口:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
//省略非核心代码......
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//这里如果是异步请求的话,则直接 return,响应中断
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
//这里是Spring 提供的异步请求拦截器,默认实现为空,逻辑需自己实现
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
继续跟进 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 方法:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
//构造异步请求处理 manager
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
//判断是否有异步执行结果
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//开始执行请求
invocableMethod.invokeAndHandle(webRequest, mavContainer);
//这里判断当前是否有异步请求,如果有则直接返回null。
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
注意:上面有两个判断:
1) asyncManager.hasConcurrentResult()
判断是否有异步请求结果。
2) asyncManager.isConcurrentHandlingStarted()
这里判断当前请求是否是异步请求。
具体看一下第一个判断:
public boolean hasConcurrentResult() {
return (this.concurrentResult != RESULT_NONE);
}
private static final Object RESULT_NONE = new Object();
private Object concurrentResult = RESULT_NONE;
这里的 RESULT_NONE 就是一个静态final变量 object,concurrentResult 是异步任务执行结果,在异步任务执行结束,会对该变量赋值,具体后面会提到。
也就是说这两个变量如果不相等,则证明有异步任务执行结果。
接着上面的逻辑,如果没有异步执行结果,则跳过 if 中的逻辑。继续执行invocableMethod.invokeAndHandle(webRequest, mavContainer);
方法:
/**
* Invoke the method and handle the return value through one of the
* configured {@link HandlerMethodReturnValueHandler}s.
* @param webRequest the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type (not resolved)
*/
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//这里是开始调用 controller 方法,因为是返回的 callable任务,所以处理速度比快。
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//设置响应状态
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
//true表示方法调用完毕
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
//这里处理返回值,渲染页面/重定向/中断响应 都是这里的操作
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
跟进 handleReturnValue
方法:
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//这里根据参数选择对应的处理handler,从渲染页面/重定向/中断响应等handler中进行匹配
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
//处理结果
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
//选择handler
private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
//判断是否是异步请求
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
//这里可以清晰的看到 不是AsyncHandlerMethodReturnValueHandler类型,则直接跳过,通过下面的返回类型匹配。
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
到这里,逻辑逐渐清晰,异步请求的话,这里判断结果isAsyncValue为真,拿到的handler也就是:CallableMethodReturnValueHandler
,跟进该方法看一下处理逻辑:
public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object... processingContext) throws Exception {
Assert.notNull(webAsyncTask, "WebAsyncTask must not be null");
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
Long timeout = webAsyncTask.getTimeout();
if (timeout != null) {
this.asyncWebRequest.setTimeout(timeout);
}
//获取 spring 线程池,也就是说异步的请求有spring代理,发起请求。
AsyncTaskExecutor executor = webAsyncTask.getExecutor();
if (executor != null) {
this.taskExecutor = executor;
}
//这里是一些拦截器的处理
List<CallableProcessingInterceptor> interceptors = new ArrayList<CallableProcessingInterceptor>();
interceptors.add(webAsyncTask.getInterceptor());
interceptors.addAll(this.callableInterceptors.values());
interceptors.add(timeoutCallableInterceptor);
final Callable<?> callable = webAsyncTask.getCallable();
final CallableInterceptorChain interceptorChain = new CallableInterceptorChain(interceptors);
this.asyncWebRequest.addTimeoutHandler(new Runnable() {
@Override
public void run() {
logger.debug("Processing timeout");
Object result = interceptorChain.triggerAfterTimeout(asyncWebRequest, callable);
if (result != CallableProcessingInterceptor.RESULT_NONE) {
setConcurrentResultAndDispatch(result);
}
}
});
//包装异步request
this.asyncWebRequest.addCompletionHandler(new Runnable() {
@Override
public void run() {
interceptorChain.triggerAfterCompletion(asyncWebRequest, callable);
}
});
interceptorChain.applyBeforeConcurrentHandling(this.asyncWebRequest, callable);
startAsyncProcessing(processingContext);
try {
this.taskExecutor.submit(new Runnable() {
@Override
public void run() {
Object result = null;
try {
interceptorChain.applyPreProcess(asyncWebRequest, callable);
result = callable.call();
}
catch (Throwable ex) {
result = ex;
}
finally {
result = interceptorChain.applyPostProcess(asyncWebRequest, callable, result);
}
//执行结束之后的赋值操作
setConcurrentResultAndDispatch(result);
}
});
}
catch (RejectedExecutionException ex) {
Object result = interceptorChain.applyPostProcess(this.asyncWebRequest, callable, ex);
setConcurrentResultAndDispatch(result);
throw ex;
}
}
到这一步,前后呼应,整体流程已清晰浮现。上面关键的代码是赋值操作,跟进去setConcurrentResultAndDispatch(result);
:
private void setConcurrentResultAndDispatch(Object result) {
synchronized (WebAsyncManager.this) {
if (hasConcurrentResult()) {
return;
}
这里将异步执行结果,赋值给concurrentResult ,用来与RESULT_NONE 进行比较
this.concurrentResult = result;
}
if (this.asyncWebRequest.isAsyncComplete()) {
logger.error("Could not complete async processing due to timeout or network error");
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Concurrent result value [" + this.concurrentResult +
"] - dispatching request to resume processing");
}
this.asyncWebRequest.dispatch();
}
当spring 线程池再次发起请求时,上面的两个判断,第一个判断是否有异步执行结果,条件成立,将结果取出赋值。
所以此时 concurrentResult 不再等于RESULT_NONE,判断如下:
第二个判断是否是异步请求,因为此时的请求由spring发起,执行第一个请求中的 callable 任务,所以第二个请求并非异步请求,所以直接返回结果,响应前面中断的第一个请求。
到这里就结束了,整个流程对应着上面的流程图。来梳理一下。
1)当controller返回值是Callable的时候,springmvc会根据当前线程返回的结果,对请求是否是异步请求进行标记。
2)如果是异步请求,就会启动一个线程将Callable交给Spring的线程池去处理。然后DispatcherServlet还有所有的spring拦截器都退出主线程,然后把response保持打开的状态。
3)当Callable执行结束之后,然后DispatcherServlet就重新调用和处理Callable异步执行的返回结果,然后返回视图。
优点:异步请求的主要做法就是将接受请求的线程和处理业务逻辑的线程分开,保证服务器可以接收更多的线程,以免在面对一些耗时的操作,后端还有处理能力,但是没有线程来接收请求。