学习Servlet异步和非堵塞I/O

Asynchronous异步

  服务器资源是有价值的,应谨慎使用。考虑一个servlet 必须等待一个JDBC连接,或等待接收JMS消息 或从文件系统读取的资源。等待一个“长期运行”过程返回会引起 线程完全阻塞。异步
处理在在等待长时间运行的过程同时,可以使控制(或线程)返回到容器来继续执行其他
任务。

@WebServlet(urlPatterns="/async",asyncSupported=true)
public class MyAsyncServlet extends HttpServlet {

也可以在 web.xml定义<async-supported>为true。

也可以在 web.xml定义<async-supported>为true。

然后,您可以在单独的线程使用request的同步放startA方法启动异步处理
,此方法返回AsyncContext,它代表了 异步请求的执行上下文。然后你就可以通过调用AsyncContext.complete完成异步 ,或者派遣到另一个请求 资源(隐式)。容器将在后台完成异步请求的调用。

假设有一个需要长时间运行的任务:

class MyAsyncService implements Runnable {
AsyncContext ac;
public MyAsyncService(AsyncContext ac) {
this.ac = ac;
}
@Override
public void run() {
//. . .
ac.complete();
}
}

这个任务可以在servlet中被调用异步运行:

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
  AsyncContext ac = request.startAsync();
  ac.addListener(new AsyncListener() {
    public void onComplete(AsyncEvent event)
      throws IOException {
       //. . .
  }

  public void onTimeout(AsyncEvent event)  throws IOException {
        //. . .
   }
  //. . .
  });

  ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
  executor.execute(new MyAsyncService(ac));

}

该请求被放入异步模式。 当请求处理完成后,AsyncListener被注册 侦听事件,或已超时,或导致一个错误。长期运行的服务在一个单独的线程异步调用,完成请求处理调用Context.complete。

 

非堵塞Nonblocking I/O

  Servlet 3.0中允许异步请求处理,但只允许传统 I / O,这限制了应用程序的可扩展性。

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
  ServletInputStream input = request.getInputStream();
  byte[] b = new byte[1024];
  int len = -1;
  while ((len = input.read(b)) != -1) {
  //. . .
  }
}

如果传入的数据流比服务器更慢,那么 服务器线程就在等待数据。如果数据被写入,相同的可能也会发生,这限制了Web容器的可扩展性。


  非阻塞I / O使得Web容器不仅可伸缩,也可以同时处理更多连接数量。非阻塞 I / O只能用异步请求处理的Servlet,过滤器。Servlet3.1实现了非阻塞I / O,通过引入两个新的接口:ReadListener和WriteListener。这些监听者有回调方法,可以在 内容可被读取或可写入而不阻塞时调用。前面案例重写为:

AsyncContext context = request.startAsync();
ServletInputStream input = request.getInputStream();
input.setReadListener(new MyReadListener(input, context));

ReadListener有三个回调方法:

  • onDataAvailable回调方法是数据可以被读取时调用

  • onAllDataRead回调方法是当请求数据 完全读取时调用。

  • OnError回调是如果有一个错误处理请求时被调用

@Override
public void onDataAvailable() {
try {
 StringBuilder sb = new StringBuilder();
 int len = -1;
 byte b[] = new byte[1024];
 while (input.isReady() && (len = input.read(b)) != -1) {
    String data = new String(b, 0, len);
 }
 } catch (IOException ex) {
  //. . .
 }
}
@Override
public void onAllDataRead() {
 context.complete();
}
@Override
public void onError(Throwable t) {
 t.printStackTrace();
 context.complete();
}

ServletInputStream.isReady方法用于检查数据可以被读取而不会阻塞,然后数据被读出。 context.complete在 onAllDataRead和onError方法读取数据的完成后调用。 Servle
tInputStream.isFinished可以用来检查一个非阻塞I/ O读取的状态。

WriteListener有两个回调方法:

  • onWritePossible是可以无堵塞写入数据被调用时

  • onerror的是如果有错误处理响应时被调用

最多只有一个WriteListener可以在ServletOutputStream注册。 ServletOut putStream.canWrite是一种新的方法检查数据是否可以不阻塞地被写入。


你可能感兴趣的:(学习Servlet异步和非堵塞I/O)