在Servlet组件接收到的每个请求都会产生一个线程来处理请求并返回响应,当客户端的请求处理是一项比较耗时的过程,当有大量用户请求此Servlet时,Web容器中就会产生大量的线程,导致Web容器性能急剧下降。为了解决这一问题,Servlet提供了对请求的异步处理支持。
异步处理请求的过程为:当Servlet接收到请求之后,首先需要对请求携带的数据进行一些预处理;接着,Servlet线程将请求转交给一个异步线程来执行业务处理,线程本身则返回至容器并可以处理其它客户端的请求,此时Servlet并没有差生响应数据,异步处理完业务以后,可以直接生成响应数据,或者将请求继续转发给其它Servlet。这样,Servlet线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。
使用异步处理,必须要配置相应的部署文件,在web.xml中通过使用<async-supported>标签设置是否需要启用异步处理支持,true表示支持,false表示不支持。当然我们也可以用注解的方式来替代在web.xml中的配置。注解则需要使用asyncSupported属性来支持异步处理。
下面我们看一个小Demo来掩饰Servlet组件的异步处理特性:
package com.yl.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
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;
/**
* Servlet组件异步处理请求特性的演示小程序
*
* @author LYYL
*
*/
//通过注解的方式配置,支持异步处理请求
@WebServlet(name="servlet", urlPatterns={"/asyncServlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
processRequest(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
processRequest(req, resp);
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
pw.println("进入Servlet的时间是:" + new Date() + ".");
pw.flush();
//在子线程中执行业务逻辑,并由其负责输出响应,主线程退出
AsyncContext act = request.startAsync();
//创建一个模拟业务逻辑执行的线程
Executor ec = new Executor(act);
Thread td = new Thread(ec);
td.start();
pw.println("<br>");
pw.println("结束Servlet的时间是: " + new Date() + ".");
pw.flush();
}
/**
* 内部类,模拟线程执行的业务逻辑
*
* @author LYYL
*
*/
public class Executor implements Runnable{
private AsyncContext act = null;
/**
* @param act
*/
public Executor(AsyncContext act) {
this.act = act;
}
@Override
public void run() {
try {
Thread.sleep(3000);
PrintWriter pw = act.getResponse().getWriter();
pw.println("<br>");
pw.println("业务执行完的时间是: " + new Date() + ".");
pw.flush();
act.complete();
pw.close();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
最终效果,Servlet开始和结束时间基本一致,而异步线程结束的时间也就是该Servlet执行完业务逻辑的时间是我们设定的3秒之后,如图:
注意:在通过request得到AsyncContext实例的时候用的是startAsync()方法,而不是request.getAsyncContext(),如果你用的是request.getAsyncContext()得到的实例为空,会在异步线程中报空指针异常。