黑马程序员技术交流社区

标题: Servlet3.0的新特性之异步请求 [打印本页]

作者: 鱼丸儿    时间: 2018-1-20 11:37
标题: Servlet3.0的新特性之异步请求
Servlet是运行在服务器端的一段java程序,它的主要作用是接收客户端的请求,并针对这次请求做出响应。那么在一个请求过来时,Servlet的service方法就会执行,我们都知道,这个service方法的执行,是依赖于Servlet容器中的线程池中的子线程的,换句话讲,就是如果Servlet容器的线程池中没有可利用的线程池,那么这次请求要么被直接拒绝,要么处于等待状态中,这两种结果都不是用户喜欢看到的。
假设Tomcat的配置中, 同时并发的线程数为10个,最大等待的线程数为10个
下面我们先看一下没有异步Servlet的情况
[Java] 纯文本查看 复制代码
@WebServlet("/SyncServlet")
public class SyncServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
      
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                try {
                        Thread.sleep(10000);
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
        }
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                doGet(request, response);
        }
}
上面的代码非常简单,我们模拟了一个耗时10秒的请求处理,也就是说,在这10秒之内,如果有10个请求过来,那么10个请求都能被处理,如果再来10个,那么这10个就会处于等待状态,直到有请求被处理完后,对应的执行doGet方法的线程才能回到Servlet容器的线程池中,等待的请求才能被处理。假如有第21个请求在这10秒之内同时发出,那么将有一个请求会被拒绝,相信这个道理大家都能明白,因为能处理请求的线程数是有限的,那么我们是不是可以增大Servlet容器线程池大小呢?当然可以,如果该服务器上所有请求都是非常耗时的,我们可以通过配置增大Servlet容器线程池大小,但是实际开发中,并不是所有请求都非常耗时,而是某个或者某几个请求非常耗时,比如文件上传或者下载,那么这种情况下,通过配置增大Servlet容器线程池大小,就有点得不偿失了。所以我们针对这种情况,会以新开工作线程的方式来提高服务器对请求的吞吐。此时Servlet3.0的异步Servlet就派上用场了。

       下面我们将上面同步Servlet改成异步支持,如下:
[Java] 纯文本查看 复制代码
@WebServlet(urlPatterns="/SyncServlet",asyncSupported=true)
public class SyncServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
      
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                AsyncContext startAsync = request.startAsync();
                new MyThread(startAsync).start();
        }

        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                doGet(request, response);
        }
       
        class MyThread extends Thread{
                private AsyncContext context;
               
                public MyThread(AsyncContext context) {
                        this.context = context;
                }

                @Override
                public void run() {
                        //通过AsyncContext可以获取request对象,对请求数据做处理
                        HttpServletRequest request = (HttpServletRequest) context.getRequest();
                        //通过AsyncContext可以获取response对象,对响应做处理
                        HttpServletResponse response = (HttpServletResponse) context.getResponse();
                        try {
                                Thread.sleep(10000);
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                       
                        //处理完成后,一定就记得调用该方法通知Servlet容器,异步处理完成
                        context.complete();
                }
        }
}

在上述案例的代码中,我们在doGet方法中,调用了newMyThread(startAsync).start()新开一个线程,那么这个线程不依赖于Servlet容器线程池中的线程,doGet方法执行完这行代码后,doGet方法也就执行完了,那么处理doGet的线程就可以回到Servlet容器线程池中, 继续处理其他请求,这样即使有耗时的任务,也不会阻塞Servlet容器线程池中的线程,从而提高服务器响应请求的吞吐。

Tips:上述代码中,是自己创建了一个MyThread线程,在实际开发中,我们会以线程池的方式去管理这些工作线程。线程池相关内容,请自行学习。







欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2