SpringMVC创建异步回调请求的4种方式

一、4种异步回调请求的创建方式

首先要明确一点,同步请求和异步请求对于客户端用户来讲是一样的,都是需客户端等待返回结果。不同之处在于请求到达服务器之后的处理方式,下面用两张图解释一下同步请求和异步请求在服务端处理方式的不同:

SpringMVC创建异步回调请求的4种方式_第1张图片

同步请求

SpringMVC创建异步回调请求的4种方式_第2张图片

异步请求

两个流程中客户端对Web容器的请求,都是同步的。因为它们在请求客户端时都处于阻塞等待状态,并没有进行异步处理。在Web容器部分,第一个流程采用同步请求,第二个流程采用异步回调的形式。通过异步处理,可以先释放容器分配给请求的线程与相关资源,减轻系统负担,从而增加了服务器对客户端请求的吞吐量。但并发请求量较大时,通常会通过负载均衡的方案来解决,而不是异步。

  1. 使用AsyncContext执行异步请求

注意该种方式的异步回调不会触发AsyncHandlerInterceptor拦截器的afterConcurrentHandlingStarted()方法

package com.example.async;

import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AsyncContextController {

    @GetMapping("/asyncContext")
    @ResponseBody
    public String asyncTask(HttpServletRequest request) {
        AsyncContext asyncContext = request.startAsync();
        asyncContext.addListener(new AsyncListener() {

            @Override
            public void onTimeout(AsyncEvent event) throws IOException {
                System.out.println("处理超时了...");
            }

            @Override
            public void onStartAsync(AsyncEvent event) throws IOException {
                System.out.println("线程开始执行");
            }

            @Override
            public void onError(AsyncEvent event) throws IOException {
                System.out.println("执行过程中发生错误:" + event.getThrowable().getMessage());
            }

            @Override
            public void onComplete(AsyncEvent event) throws IOException {
                System.out.println("执行完成,释放资源");
            }
        });
        asyncContext.setTimeout(6000);
        asyncContext.start(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    System.out.println("内部线程:" + Thread.currentThread().getName());
                    asyncContext.getResponse().getWriter().println("async processing");
                } catch (Exception e) {
                    System.out.println("异步处理发生异常:" + e.getMessage());
                }
                asyncContext.complete(); // 异步请求完成通知,整个请求完成
            }
        });
        System.out.println("主线程:" + Thread.currentThread().getName()); 
        return "OK";
    }
}
  1. 使用Callable执行异步请求

package com.example.async;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CallableController {

    @GetMapping(path = "/callable")
    @ResponseBody
    public Callable asyncRequest() {
        return () -> {
            TimeUnit.SECONDS.sleep(10);
            return "OK";
        };
    }
}
  1. 使用WebAsyncTask执行异步请求

package com.example.async;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.WebAsyncTask;

@RestController
public class WebAsyncTaskController {

    @GetMapping("/webAsyncTask")
    @ResponseBody
    public WebAsyncTask asyncTask() {
        WebAsyncTask webAsyncTask = new WebAsyncTask(1000l * 10, new Callable() {
            @Override
            public String call() throws Exception {
                TimeUnit.SECONDS.sleep(5);
                return "OK";
            }
        });
        webAsyncTask.onCompletion(new Runnable() {
            @Override
            public void run() {
                System.out.println("调用完成");
            }
        });
        webAsyncTask.onTimeout(new Callable() {
            @Override
            public String call() throws Exception {
                return "Time Out";
            }
        });
        return webAsyncTask;
    }
}
  1. 使用DeferredResult执行异步请求

package com.example.async;

import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

@RestController
public class DeferredResultController {

    @GetMapping(path = "/deferredResult")
    @ResponseBody
    public DeferredResult asyncRequest() {
        DeferredResult deferredResult = new DeferredResult<>(1000L * 5, "失败");
        deferredResult.onTimeout(() -> {
            System.out.println("调用超时");
            deferredResult.setResult("调用超时");
        });
        deferredResult.onCompletion(() -> {
            System.out.println("调用完成");
        });
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(10);
                deferredResult.setResult("OK");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        return deferredResult;
    }
}
  1. 另外:Spring Boot中使用注解@Async处理异步任务

@Async注解的异步操作和上文所诉的四种异步请求不同之处在于,使用@Async处理异步任务时没有异步回调响应客户端的流程:

使用@EnableAsync开启@Async

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@SpringBootApplication
public class ExampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }

}

如果将@Async加在Controller上或是 Controller 的方法上

package com.example.async;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AsyncController {

    @Autowired
    private TestService testService;

    @GetMapping("/async")
    @ResponseBody
    @Async
    public String asyncTask() {
        testService.doSomeThing();
        System.out.println("处理完成");
        return "OK";
    }
}

控制器立即会给客户端空响应,但是控制器方法依旧执行

SpringMVC创建异步回调请求的4种方式_第3张图片
SpringMVC创建异步回调请求的4种方式_第4张图片

如果将@Async加在Service上或是 Service 的方法上

package com.example.async;

import java.util.concurrent.TimeUnit;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class TestService {
    @Async
    public void doSomeThing() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

控制器不再等待Service方法执行完毕就响应客户端

SpringMVC创建异步回调请求的4种方式_第5张图片
二、异步回调请求的执行流程以及部分自定义配置

SpringMVC的核心调度器是DispatcherServlet,请求在到达DispatcherServlet父类FrameworkServlet.processRequest()方法中,调用DispatcherServlet.doService()方法之前会创建异步拦截器管理器WebAsyncManager,并注入异步拦截器,方法代码如下:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;

        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = buildLocaleContext(request);

        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
        // 创建异步拦截器管理器
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        //设置拦截器
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

        initContextHolders(request, localeContext, requestAttributes);

        try {
            //执行子类的DispatcherServlet.doService()
            doService(request, response);
        }
        catch (ServletException | IOException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }

        finally {
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
            logResult(request, response, failureCause, asyncManager);
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

下面接着分析创建拦截器的WebAsyncUtils.getAsyncManager()方法,代码如下:

    public static WebAsyncManager getAsyncManager(ServletRequest servletRequest) {
        WebAsyncManager asyncManager = null;
        Object asyncManagerAttr = servletRequest.getAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE);
        if (asyncManagerAttr instanceof WebAsyncManager) {
            asyncManager = (WebAsyncManager) asyncManagerAttr;
        }
        if (asyncManager == null) {
            //为空时,创建一个新的WebAsyncManager
            asyncManager = new WebAsyncManager();
            //设置到request的Attribute中
            servletRequest.setAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, asyncManager);
        }
        return asyncManager;
    }

异步请求的超时时间,线程池,拦截器会在RequestMappingHandlerAdapter.invokeHandlerMethod( )方法中适配:

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);
            if (this.argumentResolvers != null) {
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
                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);
            //创建异步request StandardServletAsyncWebRequest
            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            //设置超时时间
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);
            //获取异步管理器
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            //设置taskExecutor
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            //设置Callable的拦截器
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            //设置DeferredResult的拦截器
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
           //异步执行,已经解析出结果,发生在该请求接口执行完后的第二次请求结果,获取执行结果
            if (asyncManager.hasConcurrentResult()) {
                Object result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(logger, traceOn -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            //如果是异步执行,返回null
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }

自定义配置

@Configuration
@EnableAsync
public class ThreadPoolConfig {

    @Bean("taskExecutor")
    public Executor taskExecutor() {
        int processors = Runtime.getRuntime().availableProcessors();
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(processors);
        executor.setMaxPoolSize(processors * 2);
        executor.setQueueCapacity(10000);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("taskExecutor-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        return executor;
    }
}
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private AsyncTaskExecutor taskExecutor;

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        //设置默认超时时间
        configurer.setDefaultTimeout(20000);
        //设置线程池
        configurer.setTaskExecutor(taskExecutor);
        //设置拦截器
//        configurer.registerCallableInterceptors();
//        configurer.registerDeferredResultInterceptors();
    }
    
}

你可能感兴趣的:(spring,java,spring,boot)