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

rat*_*olt 20 java servlets nio jetty nonblocking

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

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

  1. 请求到达
  2. 分配一个线程来处理请求,并doGet调用servlet方法(etc)
  3. Servlet方法交给InputStreamOutputStream
  4. servlet方法从中读取InputStream并写入OutputStream
  5. InputStreamOutputStream基本上与基础的相应流Socket

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

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

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

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

我不确定我明白是什么Synchronization simulates blocking for the servlet API意思?

gre*_*egw 32

你没有完全正确.当jetty使用NIO连接器(并且9仅支持NIO)时,它的工作方式如下:

  1. 空闲状态为几个线程(1-4取决于#core)调用选择器,寻找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如何处理异步和调度的更多信息,请参阅: