使用 NIO 连接器部署到 Tomcat 时,使用 Servlet 3.0 异步是否多余?
Is using Servlet 3.0 async redundant when deployed to Tomcat with the NIO connector?
我看到了这个帖子: 但它似乎与 Servlet 3.1 NIO(相对于 Tomcat NIO HTTP 连接器)有关。
据我了解,使用 NIO HTTP 连接器实现配置 Tomcat(Tomcat 8 及更高版本中的默认设置)处理请求和生成响应的实际工作是在单独的服务器上完成的工作线程,以及 read/write 数据保持畅通无阻的轮询线程。
这似乎与 Async Servlet 3.0 解决的问题相同,因为在 request/response 上完成的工作是在独立于 http 连接线程的工作线程上完成的。
那么它们是同一个问题的两种解决方案吗?换句话说,如果 servlet 容器已经是异步的,那么以异步方式编写我的代码有什么好处吗?
通过了解在从容器到应用程序代码的请求处理的不同点上可能发生 IO 的地方,可以更容易地理解这一点。容器连接器(BIO / NIO)的工作是接受套接字连接并将其移交给线程,该线程在某个时间点调用 Servlet GET/POST 方法。现在 Tomcat NIO 连接器基本上是容器决定使用 Java NIO 设施(Selector / Channel)来处理具有较少线程的多个 IO 通道。 Selector
提供了一种机制来监视一个或多个 NIO 通道并识别一个或多个通道何时可用于数据传输,因此选择器容器可以使用一个线程而不是多个线程来管理多个通道。然后,这些就绪通道由数量可能少于 BIO 连接器所需数量的线程提供服务。
顺便说一句 - 在此级别进行了 OS 级优化以改进 NIO 功能。例如,Java 附带了一个基于 Linux epoll 事件通知工具的 java.nio.channels.SelectorProvider
实现。 epoll
工具在 Linux 2.6 和更新的内核中可用。当有数千个 SelectableChannels 注册到 Selector 时,新的基于 epoll 的 SelectorProvider 实现比传统的基于轮询的 SelectorProvider 实现更具可扩展性。当检测到 2.6 内核时,将默认使用新的 SelectorProvider 实现。
当检测到 pre-2.6 内核时,将使用基于轮询的 SelectorProvider。
现在回到手头的问题。在 Servlet 3.0 之前,整个 servlet 处理是同步的,这在 Servet 3.0 中得到了改进,因此 GET/POST 方法现在 returns 立即但不写入响应,除非通过调用 [=13= 将其视为完成] complete
。到目前为止,一切都很好。但是还有另一个问题。 Servlet 处理可能包括 reading/writing 以输入 stream/output 请求可用的流。这种 IO 的性质仍然是传统的。 Servlet 3.0 允许异步请求处理,但 IO read/write 仍然是老式的,例如,当从客户端以低速读取大请求负载时,线程会阻塞等待数据 - 所以 Servlet 3.1 非阻塞 IO 到救援现在你可以在数据准备好时调用你的 reading/writing 逻辑,而不是等待它的线程。这在代码中是如何工作的可以看到 here.
我们仍然要记住数据库 IO(如果有的话)仍然是传统的等待 IO。该区域由遵守 JDBC API 的数据库驱动程序控制,谁知道有一天他们也会为非阻塞 IO 提供 API。 Oracle 已经在领导一项这样的计划 Asynchronous Database Access,它看起来也正在利用 Java NIO。这将为使整个请求处理成为非阻塞铺平道路。
此外,Spring - Reactive relational database connectivity 正在做另一项有前途的工作,旨在解决这个问题。
我看到了这个帖子:
据我了解,使用 NIO HTTP 连接器实现配置 Tomcat(Tomcat 8 及更高版本中的默认设置)处理请求和生成响应的实际工作是在单独的服务器上完成的工作线程,以及 read/write 数据保持畅通无阻的轮询线程。
这似乎与 Async Servlet 3.0 解决的问题相同,因为在 request/response 上完成的工作是在独立于 http 连接线程的工作线程上完成的。
那么它们是同一个问题的两种解决方案吗?换句话说,如果 servlet 容器已经是异步的,那么以异步方式编写我的代码有什么好处吗?
通过了解在从容器到应用程序代码的请求处理的不同点上可能发生 IO 的地方,可以更容易地理解这一点。容器连接器(BIO / NIO)的工作是接受套接字连接并将其移交给线程,该线程在某个时间点调用 Servlet GET/POST 方法。现在 Tomcat NIO 连接器基本上是容器决定使用 Java NIO 设施(Selector / Channel)来处理具有较少线程的多个 IO 通道。 Selector
提供了一种机制来监视一个或多个 NIO 通道并识别一个或多个通道何时可用于数据传输,因此选择器容器可以使用一个线程而不是多个线程来管理多个通道。然后,这些就绪通道由数量可能少于 BIO 连接器所需数量的线程提供服务。
顺便说一句 - 在此级别进行了 OS 级优化以改进 NIO 功能。例如,Java 附带了一个基于 Linux epoll 事件通知工具的 java.nio.channels.SelectorProvider
实现。 epoll
工具在 Linux 2.6 和更新的内核中可用。当有数千个 SelectableChannels 注册到 Selector 时,新的基于 epoll 的 SelectorProvider 实现比传统的基于轮询的 SelectorProvider 实现更具可扩展性。当检测到 2.6 内核时,将默认使用新的 SelectorProvider 实现。
当检测到 pre-2.6 内核时,将使用基于轮询的 SelectorProvider。
现在回到手头的问题。在 Servlet 3.0 之前,整个 servlet 处理是同步的,这在 Servet 3.0 中得到了改进,因此 GET/POST 方法现在 returns 立即但不写入响应,除非通过调用 [=13= 将其视为完成] complete
。到目前为止,一切都很好。但是还有另一个问题。 Servlet 处理可能包括 reading/writing 以输入 stream/output 请求可用的流。这种 IO 的性质仍然是传统的。 Servlet 3.0 允许异步请求处理,但 IO read/write 仍然是老式的,例如,当从客户端以低速读取大请求负载时,线程会阻塞等待数据 - 所以 Servlet 3.1 非阻塞 IO 到救援现在你可以在数据准备好时调用你的 reading/writing 逻辑,而不是等待它的线程。这在代码中是如何工作的可以看到 here.
我们仍然要记住数据库 IO(如果有的话)仍然是传统的等待 IO。该区域由遵守 JDBC API 的数据库驱动程序控制,谁知道有一天他们也会为非阻塞 IO 提供 API。 Oracle 已经在领导一项这样的计划 Asynchronous Database Access,它看起来也正在利用 Java NIO。这将为使整个请求处理成为非阻塞铺平道路。
此外,Spring - Reactive relational database connectivity 正在做另一项有前途的工作,旨在解决这个问题。