zoukankan      html  css  js  c++  java
  • Servlet3.0对异步处理的支持

    Servlet工作流程

    Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:

    1. Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;
    2. 调用业务接口的某些方法,以完成业务处理;
    3. 最后,根据处理的结果提交响应,Servlet 线程结束。

    servlet是单例多线程的,当servlet线程池满了的时候,后续访问该Servlet的请求只能进行等待

    其中第二步的业务处理通常是最耗时的,这主要体现在数据库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源。

    Servlet3.0异步处理

    Servlet 3.0 针对这个问题做了开创性的工作,现在通过使用 Servlet 3.0 的异步处理支持,之前的 Servlet 处理流程可以调整为如下的过程:

    1. 首先,Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;
    2. 接着,Servlet 线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器,此时 Servlet 还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有 ServletRequest 和 ServletResponse 对象的引用),或者将请求继续转发给其它 Servlet。如此一来, Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。

    代码示例一:

    首先创建一个动态的web工程

    public class DemoThread implements Runnable {
        private PrintWriter out;
    
        public DemoThread(PrintWriter out) {
            this.out = out;
        }
    
        @Override
        public void run() {
            out.println("子线程---运行<br>");
            int sum = 0;
            for (int i = 0; i < 10; i++) {
                System.out.println("i = " + i);
                sum += i;
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            out.println("sum = " + sum + "<br>");
            out.println("子线程---结束<br>");
        }
    }
    
    
    @WebServlet("/demo")
    public class DemoServlet extends HttpServlet {
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
            out.print("主线程开始运行<br>");
            new Thread(new DemoThread(out)).start();
            out.print("主线程运行结束<br>");
        }
    }
    

    使用Servelt3.0注解方式创建的一个DemoServlet,我们运行tomcat容器并访问这个Servlet

    观察控制台:

    这与我们需要的展示效果相差甚远 :主线程直接就结束了,子线程依旧在运行


    注意:

    异步处理特性可以应用于 Servlet 和过滤器两种组件,由于异步处理的工作模式和普通工作模式在实现上有着本质的区别,因此默认情况下,Servlet 和过滤器并没有开启异步处理特性,如果希望使用该特性,则必须按照如下的方式启用:

    我们开启Servelt异步处理后,改写代码观察结果:

    // asyncSupported 默认为false,
    @WebServlet(value = "/demo",asyncSupported = true)
    public class DemoServlet extends HttpServlet {
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
            out.println("进入Servlet的时间:" + new Date() + "<br>");
    
            //在子线程中执行业务调用,并由其负责输出响应,主线程退出
            AsyncContext ctx = request.startAsync();
            new Thread(new DemoThread(ctx)).start();
            out.println("结束Servlet的时间:" + new Date() + "<br>");
        }
    }
    
    public class DemoThread implements Runnable {
    
        private AsyncContext ctx = null;
    
        public DemoThread(AsyncContext ctx) {
            this.ctx = ctx;
        }
    
        @Override
        public void run() {
            int sum = 0;
            try {
                // 通过此种方式就能获取到响应流
                PrintWriter out = ctx.getResponse().getWriter();
                out.println("子线程---运行<br>");
                sum = 0;
                for (int i = 0; i < 10; i++) {
                    System.out.println("i = " + i);
                    sum += i;
                    Thread.sleep(1000L);
                }
                out.println("sum = " + sum + "<br>");
                out.println("子线程---结束<br>");
                out.println("业务处理完毕的时间:" + new Date() + "<br>");
                ctx.complete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    观察时间可以发现,Servlet线程运行完就结束了,返回到线程池,大大节省了资源,如果有多个请求,就能够处理接下来的请求。而我们的DemoThread线程依旧能够生成响应数据

  • 相关阅读:
    一个docker容器中运行多个服务还是弄一堆docker容器运行?
    golang配置 GoGetProxyConfig,goproxy.io的介绍
    Docker下运行Mysql报错 mbind: Operation not permitted
    linux允许root用户远程登录
    docker-compose 安装 mysql并初始化用户与sql文件
    spring报错 xxxxxxxxxxxx has been injected into other beans
    mysql报错[Warning] IP address 'xxxx' could not be resolved: Name or service not known错误解决
    idea 高效找出全部未被使用的代码
    springboot配置Filter的两种方法
    .net core Elasticsearch 查询更新
  • 原文地址:https://www.cnblogs.com/heliusKing/p/11620958.html
Copyright © 2011-2022 走看看