• Spring注解开发系列Ⅸ --- 异步请求


    一. Servlet中的异步请求

    在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理。如果要处理一些IO操作,以及访问数据库,调用第三方服务接口时,这种做法是十分耗时的。可以用代码测试一下:

    同步方式处理: 

    @WebServlet("/helloServlet")
    public class HelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println(Thread.currentThread()+"start...");
            try {
                sayHello();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resp.getWriter().write("hello...");
            System.out.println(Thread.currentThread()+" end ....");
    
        }
        public void  sayHello() throws InterruptedException {
            Thread.sleep(3000);
        }
    }

    以上代码,执行了一个3秒的方法,主线程在3秒方法执行完之后,才得到释放,这样处理效率非常不高。在Servlet 3.0引入了异步处理,然后在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。

    异步方式处理业务逻辑:

    @WebServlet(value = "/asyncServlet",asyncSupported = true)
    public class AsyncHelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println(Thread.currentThread()+"主线程start..."+System.currentTimeMillis());
            //1.支持异步处理 asyncSupported = true
            //2.开启异步
            AsyncContext asyncContext = req.startAsync();
            //3.业务逻辑业务处理
            asyncContext.start(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread()+"副线程start..."+System.currentTimeMillis());
                        sayHello();
                        asyncContext.complete();
    
                        //4.获取响应
                        ServletResponse response = asyncContext.getResponse();
                        response.getWriter().write("hello,async...");
                        System.out.println(Thread.currentThread()+"副线程end..."+System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            System.out.println(Thread.currentThread()+"主线程end..."+System.currentTimeMillis());
    
            //测试结果,主线程接受请求处理,并立即得到释放,副线程处理业务逻辑
            /**
             * Thread[http-apr-8080-exec-9,5,main]主线程start...1545911791802
             * Thread[http-apr-8080-exec-9,5,main]主线程end...1545911791806
             * Thread[http-apr-8080-exec-10,5,main]副线程start...1545911791806
             * Thread[http-apr-8080-exec-10,5,main]副线程end...1545911794807
             */
        }
        public void  sayHello() throws InterruptedException {
            Thread.sleep(3000);
        }

    上面的代码中,主线程在Servlet被执行到时,立刻得到了释放,然后在副线程中调用了sayHello方法,3秒后副线程得到释放,这就是异步Servlet最简单的一个demo演示。

    . springmvc中的异步请求

    1.返回Callable<>接口

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

    2)DispacherServlet和所有的Filter退出线程,但是response保持打开状态

    3)Callbale返回结果,springMVC将请求重新派发给容器,恢复之前的处理

    4)根据Callable返回的结果,springMVC继续进行视图渲染流程等(从请求-视图渲染)。

    代码如下:

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

    测试运行结果:

          MyInterceptor preHandle..
          拦截器拦截的请求:/callable
          主线程Thread[http-apr-8080-exec-9,5,main]Start======>1545965335805
          主线程Thread[http-apr-8080-exec-9,5,main]End======>1545965335805
          ==============DispacherServlet及所有的Filter退出线程=============
          ========================等待Callable执行========================
          副线程Thread[MvcAsync1,5,main]Start======>1545965335811
          副线程Thread[MvcAsync1,5,main]End======>1545965338811
          ========================Callable执行完成========================
          MyInterceptor preHandle..
          拦截器拦截的请求:/callable
          MyInterceptor postHandle..(Callable的之前的返回值就是目标方法的返回值)
          MyInterceptor afterCompletion..

    2.使用DeferredResult<>实现异步

    public  class DeferredResultQueue {
        public static Queue<DeferredResult<Object>> queue=new ConcurrentLinkedDeque<>();
        public static void save(DeferredResult<Object> deferredResult){
            queue.add(deferredResult);
        }
        public static DeferredResult<Object> get(){
            return queue.poll();
        }
    }
    @Controller
    public class MyDeferredResultController{
        /**
         * 一个线程创建订单,一个线程等待处理订单
         * @return
         */
        @ResponseBody
        @RequestMapping("/createOrder")
        public DeferredResult<Object> createOrder(){
            //设置等待时间,3秒后等待超时
            DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000,"createFail");
            DeferredResultQueue.save(deferredResult);
            return deferredResult;
        }
        @ResponseBody
        @RequestMapping("/create")
        public String create(){
            String order = UUID.randomUUID().toString();
            DeferredResult<Object> deferredResult = DeferredResultQueue.get();
            deferredResult.setResult(order);
            return "success===>"+order;
        }
    }

    上述代码模拟了创建订单的过程,运行时,先访问/createOrder请求,该请求会等待3秒(3秒期间内没有订单创建会超时),然后访问另一个请求/create,该请求创建号订单后,/createOrder立即接收到了刚创建好的订单,两者是异步执行的。

  • 相关阅读:
    Luogu 4841 城市规划
    Luogu 4721 【模板】分治 FFT
    Luogu 4091 [HEOI2016/TJOI2016]求和
    Luogu 3723 [AH2017/HNOI2017]礼物
    FFT笔记
    Luogu 4900 食堂
    Luogu 4155 [SCOI2015]国旗计划
    Luogu 4069 [SDOI2016]游戏
    Luogu 4254 [JSOI2008]Blue Mary开公司
    Luogu 4251 [SCOI2015]小凸玩矩阵
  • 原文地址:https://www.cnblogs.com/wangxiayun/p/10187135.html
Copyright © 2020-2023  润新知