Servlet3.0中支持的异步处理

简介: Servlet3.0中支持的异步处理

【1】HelloAsyncServlet

在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理。


如果一个请求需要进行IO操作,比如访问数据库、调用第三方服务接口等,那么其所对应的线程将同步地等待IO操作完成, 而IO操作是非常慢的,所以此时的线程并不能及时地释放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题。


为了解决这样的问题,Servlet 3.0引入了异步处理,然后在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。



实例代码

asyncSupported=true开启异常支持,默认为false

@WebServlet(value="/async",asyncSupported=true)
public class HelloAsyncServlet extends HttpServlet {  
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1、支持异步处理asyncSupported=true
    //2、开启异步模式
    System.out.println("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
    AsyncContext startAsync = req.startAsync();
    //3、业务逻辑进行异步处理;开始异步处理
    startAsync.start(new Runnable() {
      @Override
      public void run() {
        try {
          System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
          sayHello();
          //获取到异步上下文
          AsyncContext asyncContext = req.getAsyncContext();
          //4、获取响应
          ServletResponse response = asyncContext.getResponse();
          response.getWriter().write("hello async...");
          System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
          startAsync.complete();
        } catch (Exception e) {
        }
      }
    });   
    System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
  }
  public void sayHello() throws Exception{
    System.out.println(Thread.currentThread()+" processing...");
    Thread.sleep(3000);
  }
}

测试结果如下:

主线程开始。。。Thread[http-apr-8080-exec-7,5,main]==>1524209187508
主线程结束。。。Thread[http-apr-8080-exec-7,5,main]==>1524209187560
副线程开始。。。Thread[http-apr-8080-exec-9,5,main]==>1524209187560
Thread[http-apr-8080-exec-9,5,main] processing...
asyncContext..org.apache.catalina.core.AsyncContextImpl@e9f2202
副线程结束。。。Thread[http-apr-8080-exec-9,5,main]==>152420919900

【2】ThreadPoolAsyncHelloServlet

自己手动创建新线程一般是不被鼓励的,并且此时线程不能重用。因此,一种更好的办法是我们自己维护一个线程池。这个线程池不同于Servlet容器的主线程池,如下图:


实例代码如下:

@WebServlet(value = "/threadPoolAsync", asyncSupported = true)
public class ThreadPoolAsyncHelloServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
  private static ThreadPoolExecutor executor = 
  new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        AsyncContext startAsync = request.startAsync();
        executor.execute(new Runnable() {
      @Override
      public void run() {
        try {
          System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
          sayHello();
          //获取到异步上下文
          AsyncContext asyncContext = request.getAsyncContext();
          //4、获取响应
          ServletResponse response = asyncContext.getResponse();
          response.getWriter().write("hello async...");
          System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
          startAsync.complete();
        } catch (Exception e) {
        }
      }
    });
    }
    public void sayHello() throws Exception{
    System.out.println(Thread.currentThread()+" processing...");
    Thread.sleep(3000);
  }
}


测试结果如下:

副线程开始。。。Thread[pool-1-thread-1,5,main]==>1524210139124
Thread[pool-1-thread-1,5,main] processing...
副线程结束。。。Thread[pool-1-thread-1,5,main]==>1524210142125


【3】NonBlockingAsyncHelloServlet

Servlet 3.0对请求的处理虽然是异步的,但是对InputStreamOutputStream的IO操作却依然是阻塞的,对于数据量大的请求体或者返回体,阻塞IO也将导致不必要的等待。

因此在Servlet 3.1中引入了非阻塞IO(参考下图红框内容),通过在HttpServletRequest和HttpServletResponse中分别添加ReadListener和WriterListener方式,只有在IO数据满足一定条件时(比如数据准备好时),才进行后续的操作。


实例代码如下:

import javax.servlet.AsyncContext;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@WebServlet(value = "/nonBlockingThreadPoolAsync", asyncSupported = true)
public class NonBlockingAsyncHelloServlet extends HttpServlet {
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        AsyncContext startAsync = request.startAsync();
        ServletInputStream inputStream = request.getInputStream();
        inputStream.setReadListener(new ReadListener() {
            @Override
            public void onDataAvailable() throws IOException {
            }
            @Override
            public void onAllDataRead() throws IOException {
                executor.execute(new Runnable() {
              @Override
              public void run() {
                try {
                  System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
                  sayHello();
                  //获取到异步上下文
                  AsyncContext asyncContext = request.getAsyncContext();
                  //4、获取响应
                  ServletResponse response = asyncContext.getResponse();
                  response.getWriter().write("hello async...");
                  System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
                  startAsync.complete();
                } catch (Exception e) {
                }
              }
            });
            }
            @Override
            public void onError(Throwable t) {
              startAsync.complete();
            }
        });
    }
    public void sayHello() throws Exception{
    System.out.println(Thread.currentThread()+" processing...");
    Thread.sleep(3000);
  }
}



目录
相关文章
|
4月前
|
存储 Java 容器
SpringMVC与Servlet3.0整合实现异步处理
SpringMVC与Servlet3.0整合实现异步处理
45 1
|
Java 数据库 容器
Servlet异步处理性能优化的过程
Servlet异步处理性能优化的过程
173 0
Servlet异步处理性能优化的过程
厉害了,Servlet3的异步处理机制
Servlet3发布好几年了,又有多少人知道它的新特性呢?下面简单介绍下。
91 0
厉害了,Servlet3的异步处理机制
Servlet3发布好几年了,又有多少人知道它的新特性呢?下面简单介绍下。 主要增加了以下特性: 1、异步处理支持 2、可插性支持 3、注解支持,零配置,可不用配置web.xml ... 异步处理是什么鬼? 直接操起键盘干。
944 0
|
Java 容器 调度
Servlet 异步处理
web容器会为每个请求分配一个线程,Servlet3.0新增了异步处理,解决多个线程不释放占据内存的问题。可以先释放容器分配给请求的线程与相关资源,减轻系统负担,原先释放了容器所分配线程的请求,其响应将被延后,可以在处理完成后再对客户端进行响应。
848 0
|
29天前
|
Java
学校教师管理系统【JSP+Servlet+JavaBean】(Java课设)
学校教师管理系统【JSP+Servlet+JavaBean】(Java课设)
21 1
|
29天前
|
Java
人事管理系统【JSP+Servlet+JavaBean】(Java课设)
人事管理系统【JSP+Servlet+JavaBean】(Java课设)
19 0
|
1月前
使用Servlet上传多张图片——前台页面层(Index.jsp)
使用Servlet上传多张图片——前台页面层(Index.jsp)
14 0
|
6天前
|
设计模式 存储 前端开发
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式