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

Asynchronous异步

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

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

java

也能够在 web.xml定义<async-supported>为true。web

也能够在 web.xml定义<async-supported>为true。服务器

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

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

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

这个任务能够在servlet中被调用异步运行:ui

@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 {
        //. . .
   }
  //. . .
  });this

  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是一种新的方法检查数据是否能够不阻塞地被写入。

相关文章
相关标签/搜索