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线程,在实际开发中,我们会以线程池的方式去管理这些工作线程。线程池相关内容,请自行学习。
|