乐趣区

1713-非阻塞-IO

应用程序的 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 所必需的。然后,服务方法获得与请求相关联的输入流,并分配定义为内部类的读取侦听器。侦听器在可用时读取部分请求,然后在完成读取请求时将一些响应写入客户端。

退出移动版