zoukankan      html  css  js  c++  java
  • Servlet3.0的异步

    servlet之前的操作同时同步的,就是按照这样的一个流程来走的:

    1.请求根据一个路径路由到一个servlet中,

    2.servlet获取一系列的参数

    3.执行一系列的逻辑(花费时间所占的比重也更大)

    4.返回结果

    上面的问题出现在这一系列的操作都是同步的,所以这个请求必定是堵塞到所以任务都完成之后才返回的,

    这样将会很浪费资源,因为线程堵塞在那里,仅仅是等待任务的完成。但是在servlet3.0之后,我们基本上可以

    是这样做的

    1.请求根据一个路径路由到一个servlet中,

    2.将逻辑放入到异步队列中去

    3.返回结果

    4.异步队列处理任务,得出结果,返回给页面

    而servet3.0对于异步的处理主要涉及的有两个特性,一个是新增的类AsyncContext,另外的一个就是asyncSupported属性

    ①如果我们想要让我们的servlet支持异步的话,那么asyncSupported这个属性是一定需要设置的,对于注解的类型来说,我们直接设置属性

    @WebServlet(asyncSupported=true,urlPatterns={"/async"})
    

    就可以了,对于老版本的配置问价来说,只需要在配置web.xml 的servlet那里增加一个

    <async-supported>true</async-supported> 

    还有一个就是对于动态的servlet,设置

    dynamic.setAsyncSupported(true);
    就可以了
    ②而对于AsyncContext 需要记住的东西还是蛮多的,但是它主要的是保留了请求和相应的引用,在前面提到的返回结果之后的操作就是通过在异步环境下,对这两个引用进行操作。

    要获取这个就需要使用request在3.0之后增加的方法,startAsync(..) ,这个方法就是返回一个AsyncContext实体对象,这里包含了request和response的引用,至于我们异步的处理方式,就有很多种了,我们可以直接定义一个工作队列,异步的方式一个个的进行处理,又或者是直接使用AsyncContext.start(Runnable)方法启动一个新的线程去进行处理逻辑

    AsyncContext主要的方法:
    getRequest() 获得请求即request,我们可以在异步的环境像在service中使用一样

    getReponse() 和上面差不多一个意思

    hasOriginalRequestAndResponse()这个方法表示的是我们使用的AsyncContext是使用原始的请求获取的,还是通过封装过的请求和相应创建的
    简单的讲就是 原始的类型表示的是调用startAsync()。但是封装的就是startAsync(ServletRequest, ServletResponse)或者其他类型啦,

    dispatch()方法,这个方法有有好几个重载,表示的是转发,和req.getRequestDispatcher()有点类似,但是比较丰富
    如果使用的是startAsync(ServletRequest, ServletResponse)初始化AsyncContext,且传入的请求是HttpServletRequest的一个实例,则使用HttpServletRequest.getRequestURI()返回的URI进行分派。否则分派的是容器最后分派的请求URI。
    下面的代码是网上的:
    // 请求到 /url/A  
    AsyncContext ac = request.startAsync();  
    ...  
    ac.dispatch(); // 异步分派到 /url/A  
    
    // 请求到 /url/A  
    // 转发到 /url/B  
    request.getRequestDispatcher(“/url/B”).forward(request, response);  
    // 从FORWARD的目标内启动异步操作  
    AsyncContext ac = request.startAsync();  
    ac.dispatch(); // 异步分派到 /url/A  
    
    // 请求到 /url/A  
    // 转发到 /url/B  
    request.getRequestDispatcher(“/url/B”).forward(request, response);  
    // 从FORWARD的目标内启动异步操作  
    AsyncContext ac = request.startAsync(request, response);  
    ac.dispatch(); //异步分派到 /url/B  

    dispatch(String path) 这个方法就是转发到指定的url上去

    complete():在我们使用了request.startAsync(..)获得AsyncContext之后,在完成异步操作以后,需要调用这个方法结束异步的操作。如果请求分派到一个不支持异步操作的Servlet,或者由AsyncContext.dispatch调用的目标servlet之后没有调用complete,则complete方法会由容器调用。但是对于比合法操作来说,比如没有调用startAsync放方法,却代用complete() ,那么就会抛出IllegalStateException的异常,同时在调用complete()之前,调用dispath()方法是不起作用的,当然了,因为这个时候异步还没结束嘛,当然不会又什么作用了。

    setTimeOut(..) 设置超时的时间 表示的是异步处理的最大时间,如果是一个负数的话,那么表示永远不会超时

    start(Runnable run) Runnable表示的就是异步处理的任务。我们在做的时候 会AsyncContext  带进去 因为所以的操作 都需要依靠他呢

    addListener(AsyncListener listener);增加监听器  就是监听AsyncContext各种状态发现变化的,主要有

    前面三个都比较好理解,最后异步监听器将以它们添加到请求时的顺序得到通知。

    下面是AsyncContext的一般使用方式

    package com.hotusm.servlet.async;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.concurrent.TimeUnit;
    
    import javax.servlet.AsyncContext;
    import javax.servlet.AsyncEvent;
    import javax.servlet.AsyncListener;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet(urlPatterns={"/url"},asyncSupported=true)
    public class AsynDemoServlet extends HttpServlet{
        
        
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //resp.setHeader("Connection", "Keep-Alive");
            resp.setContentType("text/html;charset=utf-8");
            
            System.out.println(req.isAsyncSupported()+"  "+req.isAsyncStarted());
            /*req.getAsyncContext(); 表示的是最近的那个被request创建或者是
             * 重转发的AsyncContext
            */
            
            final AsyncContext ac = req.startAsync();
                
                //设置超时的时间
                ac.setTimeout(5*1000L);
                
                //这种方式 
                ac.start(new Runnable() {
                    
                    public void run() {
                        
                        try {
                            TimeUnit.SECONDS.sleep(3L);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        
                        try {
                            PrintWriter writer = ac.getResponse().getWriter();
                            writer.write("1");
                            writer.flush();
                            
                            //这是测试  同一个AsyncContext在没有调用complete 之前能不能多次的
                            //调用request 和response
                            PrintWriter writer1 = ac.getResponse().getWriter();
                            writer1.write("2");
                            writer1.flush();
                            
                            ServletRequest request = ac.getRequest();
                            
                            request.setAttribute("isAsyn", true);
                            
                            /*
                             * 2.在调用完complete之后 表示这个异步已经结束了 如果在调用
                             * getRequest 或者是getResponse的话 都会抛出IllegalStateException
                             * 
                             * */
                            ac.complete();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            //设置监听
            ac.addListener(new AsyncListenerImpl());
            
            // 在同一个request中不能同时调用多次
            //req.startAsync();
            PrintWriter out = resp.getWriter();
            out.write("hello async");
            out.write("<br/>");
            //调用flush 不然还是不会输出  因为没有将内容刷出去
            out.flush();
        }
        
        static class AsyncListenerImpl implements AsyncListener{
    
            public void onComplete(AsyncEvent event) throws IOException {
                
                System.out.println("onComplete");
            }
    
            public void onTimeout(AsyncEvent event) throws IOException {
                System.out.println("onTimeout");
                event.getAsyncContext().complete();
            }
    
            public void onError(AsyncEvent event) throws IOException {
                System.out.println("onError");
            }
    
            public void onStartAsync(AsyncEvent event) throws IOException {
                System.out.println("onStartAsync");
            }
        }
    }

    当我们上面的url的时候  会马上返回hello async,然后在大概三秒钟之后,输出12

    上面的方式只是使用了start(Runnable run);的方式.我们也可以将AsyncContext放到一个工作队列中去,然后另外的一个线程池去做处理。

     示例代码:

    package com.hotusm.servlet.async;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Map;
    import java.util.concurrent.LinkedBlockingQueue;
    
    import javax.servlet.AsyncContext;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet(urlPatterns={"/async1"},asyncSupported=true)
    public class AsyncDispatchServlet1 extends HttpServlet{
    
        private LinkedBlockingQueue<AsyncContext> works=new LinkedBlockingQueue<AsyncContext>(100);
        
        @Override
        public void init() throws ServletException {
       //因为这里是测试 所以就开了5个线程来进行处理 但是真实的情况下 肯定是设计一个伸缩性的方案
    new Thread(new HelperWork()).start(); new Thread(new HelperWork()).start(); new Thread(new HelperWork()).start(); new Thread(new HelperWork()).start(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("Connection", "Keep-Alive"); resp.addHeader("Cache-Control", "private"); resp.addHeader("Pragma", "no-cache"); resp.setContentType("text/html;charset=utf-8"); try { works.put(req.startAsync()); } catch (Exception e) { } PrintWriter writer = resp.getWriter(); writer.write("等待异步完成"); writer.flush(); } private class HelperWork implements Runnable{ public void run() { try { AsyncContext ac = works.take();

              //模拟业务消耗
              TimeUnit.SECONDS.sleep(2L)

                     HttpServletRequest request = (HttpServletRequest)ac.getRequest();

                    Map<String, String[]> maps = request.getParameterMap();
                    System.out.println(maps);
                    
                    HttpServletResponse response = (HttpServletResponse)ac.getResponse();
                    PrintWriter writer = response.getWriter();
                    writer.write(maps.toString());
                    writer.flush();
                    ac.complete();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                
            }
        }
    }

    上面只是一种思路,我们还可以放入到线程池中进行处理等等。

    然后再讲一下怎么通过ajax怎么异步的通信,我们只需要在第一次访问servlet的时候,保留AsyncContext的引用,之后通过这个的输出和页面做交互就可以了。



    
    





  • 相关阅读:
    URAL 1998 The old Padawan 二分
    URAL 1997 Those are not the droids you're looking for 二分图最大匹配
    URAL 1995 Illegal spices 贪心构造
    URAL 1993 This cheeseburger you don't need 模拟题
    URAL 1992 CVS
    URAL 1991 The battle near the swamp 水题
    Codeforces Beta Round #92 (Div. 1 Only) A. Prime Permutation 暴力
    Codeforces Beta Round #7 D. Palindrome Degree hash
    Codeforces Beta Round #7 C. Line Exgcd
    Codeforces Beta Round #7 B. Memory Manager 模拟题
  • 原文地址:https://www.cnblogs.com/zr520/p/6103410.html
Copyright © 2011-2022 走看看