• JavaWeb之异步处理请求


    1、servlet3.0-异步请求: 

      引用:在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理,当过来一个请求之后,会从tomcat的线程池中拿出一个线程去处理这个请求,处理完成之后再将该线程归还到线程池图,如图一所示。但是线程池的数量是有限的,如果一个请求需要进行IO操作,比如访问数据库(或者调用第三方服务接口等),那么其所对应的线程将同步地等待IO操作完成, 而IO操作是非常慢的,所以此时的线程并不能及时地释放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题。即便是像Spring、Struts这样的高层框架也脱离不了这样的桎梏,因为他们都是建立在Servlet之上的。为了解决这样的问题,Servlet 3.0引入了异步处理如图二,在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。

                            图一 同步模式

                            图二 异步模式

    例:

    @WebServlet(value="/async",asyncSupported=true)
    public class HelloAsyncServlet extends HttpServlet {
        
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //1、支持异步处理asyncSupported=true
            //2、开启异步模式
            System.out.println("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
            AsyncContext startAsync = req.startAsync();
            //3、业务逻辑进行异步处理;开始异步处理
            startAsync.start(() -> {
                try {
                    System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
                    sayHello();
                    startAsync.complete();
                    //获取到异步上下文
                    AsyncContext asyncContext = req.getAsyncContext();
                    //4、获取响应
                    ServletResponse response = asyncContext.getResponse();
                    response.getWriter().write("hello async...");
                    System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
                } catch (Exception e) {
                }
            });
            System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
        }
    
        public void sayHello() throws Exception{
            System.out.println(Thread.currentThread()+" processing...");
            Thread.sleep(3000);
        }
    }

    2、SpringMVC-异步请求

      Spring MVC很好的支持了Servlet 3.0异步请求,所有我们可以直接使用SpringMVC来实现异步请求:通过控制器返回DeferredResult或Callable来实现

    1)使用 Callable

       @ResponseBody
        @RequestMapping("/async01")
        public Callable<String> async01(){
            System.out.println("主线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
            
            Callable<String> callable = new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
                    Thread.sleep(2000);
                    System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
                    return "Callable<String> async01()";
                }
            };
            
            System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
            return callable;
        }

    结果:

        preHandle.../springmvc-annotation/async01
        主线程开始...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
        主线程结束...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
        =========DispatcherServlet及所有的Filter退出线程============================

        ================等待Callable执行==========
        副线程开始...Thread[MvcAsync1,5,main]==>1513932494707
        副线程开始...Thread[MvcAsync1,5,main]==>1513932496708
        ================Callable执行完成==========

        ================再次收到之前重发过来的请求========
        preHandle.../springmvc-annotation/async01
        postHandle...(Callable的之前的返回值就是目标方法的返回值)
        afterCompletion...

    
    

    说明:

      1、控制器返回Callable

      2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行

      3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态

      4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理

      5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)

    普通的拦截器不能拦截异步处理请求的方法,可以使用Servlet原生API的AsyncListener或实现SpringMVC中的AsyncHandlerInterceptor来拦截异步方法的执行

    2)使用DeferredResult

    @GetMapping("/quotes")
    @ResponseBody
    public DeferredResult<String> quotes() {
       DeferredResult
    <String> deferredResult = new DeferredResult<String>((long)3000, "timeout..."); // Save the deferredResult somewhere.. return deferredResult; } // From some other thread... deferredResult.setResult(data);

    说明:

      1)控制器返回DeferredResult

      2)可以将DeferredResult保存起来

      3)使用其他线程调用DeferredsetResult.setResult方法

      4)SpringMVC将请求重新派发给容器,恢复之前的处理

      

      

     

      

  • 相关阅读:
    VS 2010 C#入门操作小技巧
    spring-data-jpa 多数据源
    Spring--3.Spring的单元测试
    Spring--5.IOC总结
    Spring--4.实验23.IOC容器的测试泛型依赖注入*
    Spring--2.Spring之IOC--IOC容器的22个实验(3)
    Spring--2.Spring之IOC--IOC容器的22个实验(2)
    Spring--2.Spring之IOC--IOC容器的22个实验(1)
    Spring--2.Spring之IOC--了解IOC容器
    Spring--1.了解Spring
  • 原文地址:https://www.cnblogs.com/qzlcl/p/11106162.html
Copyright © 2020-2023  润新知