Jetty和其他容器如何在坚持Servlet规范的同时利用NIO?

我是NIO的新手,我正在试图弄清Jetty如何利用NIO。

我对使用阻塞IO服务请求的传统servlet容器的理解如下:

  1. 请求到达
  2. 分配一个线程来处理请求,并调用servlet方法( doGet等)
  3. Servlet方法是一个InputStreamOutputStream
  4. servlet方法从InputStream读取并写入OutputStream
  5. InputStreamOutputStream基本上与底层Socket的各个流相关联

使用NIO连接器时有何不同? 我的猜测大致如下:

  1. 请求到达
  2. Jetty使用NIO连接器并异步缓冲整个请求
  3. 一旦读取了请求,就完全将缓冲区包装在InputStream
  4. 创建一个空的响应缓冲区(包装在OutputStream
  5. 分配一个线程并调用处理上述包装器流的servlet方法( doGet等)
  6. Servlet方法写入包装(缓冲)响应流并从servlet方法返回
  7. Jetty使用NIO将响应缓冲区内容写入底层SocketChannel

从Jetty文档中,我发现了以下内容:

SelectChannelConnector – 此连接器使用具有非阻塞线程模型的高效NIO缓冲区。 Jetty使用Direct NIO缓冲区,并仅将线程分配给具有请求的连接。 同步模拟对servlet API的阻塞,并且在请求处理结束时任何未刷新的内容都是异步写入的。

我不确定我理解Synchronization simulates blocking for the servlet API意味着什么?

你没有完全正确。 当jetty使用NIO连接器(而9只支持NIO)时,它的工作原理如下:

  1. 空闲状态为几个线程(1-4取决于#核心)调用选择器,寻找IO活动。 这已经扩展到Jetty上超过1,000,000个连接。
  2. 当选择器看到IO活动时,它会在连接上调用handle方法,该方法可以是:

    • 其他东西已注册,它被阻止等待此连接的IO,所以在这种情况下,选择器只会唤醒被阻止的任何人。
    • 否则调度线程来处理连接。
  3. 如果调度线程,则它将尝试读取连接并解析它。 现在发生的事情取决于连接是http,spdy,http2还是websocket。

    • 对于http,如果请求标头完成,则线程继续调用请求的处理(最终这将到达servlet)而不等待任何内容。
    • 对于http2 / spdy,需要进行另一次调度,但请参阅列表中关于Eat-What-You-Kill策略的讨论: http : //dev.eclipse.org/mhonarc/lists/jetty-dev/msg02166.html
    • 对于websocket,调用消息处理。
  4. 一旦线程被分派到一个servlet,它看起来就像servlet IO阻塞一样,但是在HttpInputStream和HttpOutputStream的层次下,所有的IO都与回调异步。 阻塞API使用特殊的阻塞回调来实现阻塞。 这意味着如果servlet选择使用异步IO,那么它只是绕过阻塞回调并且或多或少直接使用异步API。

  5. servlet可以使用request.startAsync挂起,在这种情况下,调度的线程将返回到线程池,但关联的连接未标记为对IO感兴趣。 可以执行异步IO,但是异步循环完成后,需要重新分配线程或重新注册IO活动连接的AsyncContext事件。

http2和spdy这个视图稍微复杂,它们是多路复用的,因此它们可能涉及额外的分派。

任何不调度的HTTP框架在基准代码中都可以非常快速地运行,但是当面对一个真正的应用程序时,可以做一些愚蠢的事情,比如阻塞数据库,文件系统,REST服务等……那么缺乏调度就意味着一个连接可以阻止系统上的所有其他连接。

有关jetty如何处理异步和调度的更多信息,请参阅: