转载

17.13 非阻塞 I/O

应用程序的web容器通常为每一个客户端请求分配一个服务端线程。开发可扩展的web应用,你必须确保关联请求的线程是没有空闲的,不需要等待一个阻塞操作完成。异步处理提供了在一个新线程处理阻塞操作的机制,把关联请求的线程返回给容器。即使你所有的阻塞操作都在service方法中异步执行,关联客户端请求的线程基于input/output也可能出于暂时空闲状态。

例如,如果一个客户端在一个很慢的网络连接上提交一个大的HTTP POST请求,server读取请求的速度比客户端上传的速度快很多。使用TIO,容器关联请求的线程有时会出于空闲状态,因为要等待客户端的请求的其余部分。

JAVA EE异步模式处理请求时,提供支持servlet和filter的NIO。下面的步骤总结了在service方法中如何使用NIO处理请求和写出响应:

1. 如 Asynchronous Processing章节描述,把请求设置为异步模式  
2. 在service方法中从请求和响应对象中获得一个请求流或一个响应流
3. 分配给请求流一个监听器或者分配给响应流一个监听器 
4. 在监听器的回调方法中处理请求和响应

NIO 支持类 javax.servlet.ServletInputStream

方法签名:void setReadListener(ReadListener rl)

描述:将输入流与包含回调方法的监听器对象关联,以异步读取数据。提供的监听器对象可以是一个匿名类或者使用其他的机制给监听器对象传入输入流。

方法签名:boolean isReady()

方法描述:如果数据可以无阻塞读取,返回true

方法签名:boolean isFinished()

方法描述:当所有数据读取完毕后,返回true

NIO 支持类 javax.servlet.ServletOutputStream

方法签名:void setWriteListener(WriteListener wl)

方法描述:将此输出流与包含回调方法的侦听器对象关联,以异步写入数据。

您将写入侦听器对象提供为匿名类,或使用其他机制将输出流传递给写入侦听器对象。

方法签名:boolean isReady()

方法描述:如果数据可以无阻塞写入,返回true

NIO监听器支持接口

接口名称:ReadListener

接口方法:void onDataAvailable()、void onAllDataRead()、void onError(Throwable t)

描述:ServletInputStream当数据可以有效读取、当数据读取完毕、当发生一个错误时调用监听器的这些方法。

接口名称:WriteListener

接口方法:void onWritePossible()、void onError(Throwable t)

描述:ServletOutputStream当数据可以无阻塞读取、当发生一个错误时调用监听器的这些方法。

使用NIO读取大的HTTP POST请求

本节代码展示了在servlet对象中怎么读取一个大的HTTP POST数据,通过把请求放入异步模式中并使用NIO功能。

@WebServlet(urlPatterns={"/asyncioservlet"}, asyncSupported=true)
public class AsyncIOServlet extends HttpServlet {
   @Override
   public void doPost(HttpServletRequest request, 
                      HttpServletResponse response)
                      throws IOException {
      final AsyncContext acontext = request.startAsync();
      final ServletInputStream input = request.getInputStream();
      
      input.setReadListener(new ReadListener() {
         byte buffer[] = new byte[4*1024];
         StringBuilder sbuilder = new StringBuilder();
         @Override
         public void onDataAvailable() {
            try {
               do {
                  int length = input.read(buffer);
                  sbuilder.append(new String(buffer, 0, length));
               } while(input.isReady());
            } catch (IOException ex) { ... }
         }
         @Override
         public void onAllDataRead() {
            try {
               acontext.getResponse().getWriter()
                                     .write("...the response...");
            } catch (IOException ex) { ... }
            acontext.complete();
         }
         @Override
         public void onError(Throwable t) { ... }
      });
   }
}

此示例使用@WebServlet批注参数asyncSupported = true声明具有异步支持的Web Servlet。服务方法首先通过调用请求对象的startAsync()方法将请求置于异步模式,这是使用非阻塞I / O所必需的。然后,服务方法获得与请求相关联的输入流,并分配定义为内部类的读取侦听器。侦听器在可用时读取部分请求,然后在完成读取请求时将一些响应写入客户端。

原文  https://segmentfault.com/a/1190000019005956
正文到此结束
Loading...