Servlet3.0对异步处理的支持

Posted heliusking

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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线程依旧能够生成响应数据

以上是关于Servlet3.0对异步处理的支持的主要内容,如果未能解决你的问题,请参考以下文章

servlet3.0 新特性——异步处理

异步处理支持

Servlet 异步处理

servlet3.0 异步处理

Servlet 3.0 异步处理详解

Servlet3.0