zoukankan      html  css  js  c++  java
  • (三)Web模块:【2】Web3.0 新特性之异步处理

    一、servlet3.0异步处理

      在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求。即每一次Http请求都由某一个线程从头到尾负责处理。
      如果一个请求需要进行IO操作,比如访问数据库、调用第三方服务接口等,那么其所对应的线程将同步地等待IO操作完成, 而IO操作是非常慢的,所以此时的线程并不能及时地释放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题。即便是像Spring、Struts这样的高层框架也脱离不了这样的桎梏,因为他们都是建立在Servlet之上的。
      在以前的servlet中,如果作为控制器的servlet调用了一个较为耗时的业务方法,则servlet必须等到业务执行完后才会生成响应,这使得这次调用成了阻塞式调用,效率比较差。
      为了解决这样的问题,Servlet 3.0引入了异步处理,然后在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。
      Servlet3.0支持异步处理支持,Servlet接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;接着,Servlet线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器,此时Servlet还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有ServletRequest和ServletResponse对象的引用),或者将请求继续转发给其他Servlet。

    二、同步Servlet

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

    三、配置异步的 servlet 与 filter

      (1)通过注解asyncSupported=true实现

      (2)通过web.xml配置

        <servlet>
            <servlet-name>async</servlet-name>
            <servlet-class>com.njf.servlet.AsyncServlet</servlet-class>
            <async-suppored>true</async-suppored>       
        </servlet>
        <servlet-mapping>
            <servlet-name>async</servlet-name>
            <url-pattern>/async</url-pattern>
        </servlet-mapping>

      对于使用传统的部署描述文件web.xml配置Servlet和过滤器的情况,Servlet3.0为<servlet>和<filter>标签增加了<async-supported>子标签,该标签的默认取值为false,要启用异步处理支持,则将其设为true即可。

      对于使用Servlet3.0提供的@WebServlet和@WebFilter进行Servlet或过滤器配置的情况,这两个标注都提供了asyncSupported属性,默认该属性的取值为false,要启动异步处理支持,只需将该属性设置为true即可。

    四、代码示例

      示例1:

    @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(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
                        sayHello();
                        startAsync.complete();
                        //获取到异步上下文
                        AsyncContext asyncContext = req.getAsyncContext();
    
                        //设置异步调用超时时长
                        asyncContext.setTimeout(3*1000);
    
                        //异步Servlet里注册异步监听器
                        asyncContext.addListener(new MyAsyncListener());
    
                        //4、获取响应
                        ServletResponse response = asyncContext.getResponse();
                        PrintWriter writer = response.getWriter();
                        writer.write("hello async...");
                        writer.flush();
                        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(300);
        }
    }

      示例2:

    @WebServlet(name = "AsyncServlet", urlPatterns = {"/async01"}, asyncSupported = true)
    public class AsyncServlet01 extends HttpServlet {
    
        @Override
        public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //解决乱码
            request.setCharacterEncoding("GBK");
            response.setContentType("text/html;charset=GBK");
    
            //通过request获得AsyncContent对象
            AsyncContext actx = request.startAsync(); //重点方法**
    
            //异步Servlet里注册异步监听器
            actx.addListener(new MyAsyncListener());
    
            //设置异步调用超时时长
            actx.setTimeout(30 * 3000);
            //启动异步调用的线程
            actx.start(new MyThread(actx));//重点方法**
    
            // 直接输出到页面的内容(不等异步完成就直接给页面)
            //但这些内容必须放在标签内,否则会在页面输出错误内容,这儿反正我测试是这样,具体不知对不对??
            PrintWriter out = response.getWriter();
            out.println("<h1>不等异步返回结果就直接返到页面的内容</h1>");
            out.flush();
        }
    }
    
    //异步处理业务的线程类
    class MyThread implements Runnable {
        private AsyncContext actx;
    
        //构造
        public MyThread(AsyncContext actx) {
            this.actx = actx;
        }
    
        @Override
        public void run() {
            try {
                //等待5秒,模拟处理耗时的业务
                Thread.sleep(4 * 1000);
                //获得request对象,添加数据给页面
                ServletRequest req = actx.getRequest();
                req.setAttribute("content", "异步获得的数据");
                //将请求dispath到index.jsp页面,该页面的session必须设为false
                actx.dispatch("/index.jsp");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

      示例3:

    @WebServlet(urlPatterns = {"/async02"}, asyncSupported = true)
    public class AsyncServlet02 extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req, resp);
        }
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/html;charset=utf-8");
            PrintWriter out = resp.getWriter();
            out.println("进入Servlet的时间:" + new Date() + ".");
            out.flush();
            //在子线程中执行业务调用,并由其负责输出响应,主线程退出
            AsyncContext ctx = req.startAsync();
            new Thread(new Task(ctx)).start();
            out.println("结束Servlet的时间:" + new Date() + ".");
            out.flush();
        }
    }
    
    class Task implements Runnable {
        private AsyncContext ctx = null;
        public Task(AsyncContext ctx) {
            this.ctx = ctx;
        }
        @Override
        public void run() {
            try {
                //等待10秒钟,以模拟业务方法的执行
                Thread.sleep(10000);
                PrintWriter out = ctx.getResponse().getWriter();
                out.println("业务处理完毕的时间:" + new Date() + ".");
                out.flush();
                ctx.complete();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
  • 相关阅读:
    查找->静态查找表->次优查找(静态树表)
    P1993-小K的农场
    P1983-车站分级
    P1268-树的重量
    P1113-杂务
    P1265-公路修建
    P2330-[SCOI2005]繁忙的都市
    P1546-最短网络
    P1144-最短路计数
    P1462-通往奥格瑞玛的道路
  • 原文地址:https://www.cnblogs.com/niujifei/p/15593129.html
Copyright © 2011-2022 走看看