• SpringMVC DeferedResult和servlet3.1 AsyncContext异步请求


    先看一个简单的示例:

     1 @RequestMapping("/getFuture")
     2 public Future<String> getFuture() {
     3     System.out.println(1);
     4     // 必须是ListenableFuture的子类,或者CompletionStage子类,或者是DeferredResult 不能是FutureTask
     5     // 如何返回CompletableFuture
     6     CompletableFuture<String> ftu2 = CompletableFuture.supplyAsync(() -> {
     7         try {
     8             // RequestContextHolder.setRequestAttributes(attributes); 
     9             // 使用ThreadLocal相关特性时,需要在外面先get
    10             Thread.sleep(3000L);
    11         } catch (InterruptedException e) {
    12         } finally {
    13             // RequestContextHolder.resetRequestAttributes();
    14         }
    15         return "THIS string";
    16     }, es);
    17 
    18     //Future<String> future = executorService.submit(() -> { }); //返回值是FutureTask
    19 
    20     return ftu2;
    21 }

     




    客户端发起请求
    ===> DispatcherServlet根据返回值类型查找对应的handlar AsyncHandlerMethodReturnValueHandler
    【Callable -->
    CallableMethodReturnValueHandler; DeferredResult,CallableFuture,ListenableFuture --> DeferredResultMethodReturnValueHandler】
    ===> Controller中返回Future对象给容器, 
    org.springframework.web.context.request.async.WebAsyncManager#startDeferredResultProcessing处理Future结果的监听

    ===>
    sleep3秒之后,Future完成后调用asyncWebRequest.dispatch() 重新发给Container,DispatcherServlet找到Message的序列化器将结果输出。DispatcherServlet.doService会进入两次!!!

    异步线程的主要功能是:
    业务处理耗较长(上面的sleep3)时,可以先返回Future对象释放Container的work线程, work线程可以接收更多的请求。等Future完成之后重新调用Container的另一个work线程,输出response.

    这里work线程可以是BIO中work线程, 也可以是NIO中work线程

    不是所有的Future都能在异步线程中处理
    https://www.cnblogs.com/dennyzhangdd/p/7010972.html


    spring boot中配置container的work线程数量
    server.tomcat.max-threads=5
    server.tomcat.min-spare-threads=3
     
    使用异步处理的优点:
    增加系统吞吐量,对响应速度提高不大,可能还会降低
    缺点:
    线程关系复杂, 异步超时、异常日志拦截需要实现具体的Adapter接口,threadLocal不好清理

    参考:《亿级流量架构核心技术》http://jinnianshilongnian.iteye.com/blog/2245925
    Servlet3.1 规范
        tomcat线程配置 https://www.cnblogs.com/kismetv/p/7806063.html


    遗留问题: Future.get是什么时候调用的?
    没有直接调用get。 ListenerableFuture, CompletableFuture任务完成之后都会触发回调

    work1线程: 接收到请求---> DispatcherServlet--> return future -->WebAsyncManager#startDeferredResultProcessing
    自定义的任务线程: CompletableFuture.postComplete之后调用 WebAsyncManager#setConcurrentResultAndDispatch
    work2线程: dipatch ---> DispatcherServlet --> response.out


    关于第二个任务线程
    返回值是Callable时,调用WebAsyncManager#startCallableProcessing, 此时任务线程使用的是AsyncTaskExecutor
    可以通过org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#configureAsyncSupport设置自定义的AsyncTaskExecutor

    返回值是DeferredResult,ListenableFuture (Spring提供),CompletableFuture(JDK自带)时,调用WebAsyncManager#startDeferredResultProcessing,此时任务线程池可以直接指定
    @See CompletableFuture.supply(xxxTask, threadPool)

    @RequestMapping("/callableTest")
    public Callable<String> callableTest() {
        Callable<String> hello = () -> {
            // 此处不能设置线程池, WebAsyncManager中会使用AsyncSupportConfigurer的线程池
            System.out.println("===>" + Thread.currentThread().getName());
            return "HELLO";
        };
        return hello;
    }


  • 相关阅读:
    printf,wprintf与setlocale,char与wchar_t区别
    C++常量表达式、const、constexpr(C++11新增)的区别
    珍珠项链 Beads
    A Horrible Poem
    三个朋友
    Seek the Name, Seek the Fame
    Power Strings
    图书管理
    子串查找
    山峰和山谷 Ridges and Valleys
  • 原文地址:https://www.cnblogs.com/yszzu/p/9520590.html
Copyright © 2020-2023  润新知