Servlet3.0对异步处理的支持
Posted heliusking
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet3.0对异步处理的支持相关的知识,希望对你有一定的参考价值。
Servlet工作流程
Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:
- Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;
- 调用业务接口的某些方法,以完成业务处理;
- 最后,根据处理的结果提交响应,Servlet 线程结束。
servlet是单例多线程的,当servlet线程池满了的时候,后续访问该Servlet
的请求只能进行等待
其中第二步的业务处理通常是最耗时的,这主要体现在数据库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源。
Servlet3.0异步处理
Servlet 3.0 针对这个问题做了开创性的工作,现在通过使用 Servlet 3.0 的异步处理支持,之前的 Servlet 处理流程可以调整为如下的过程:
- 首先,Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;
- 接着,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
线程依旧能够生成响应数据
以上是关于Servlet3.0对异步处理的支持的主要内容,如果未能解决你的问题,请参考以下文章