注意:我的I / O能力不强,因此在进一步的推理中我可能是错的。

首先,我想了解一下tomcat连接器是如何工作的。
Tomcat 9具有3个连接器:NIO,APR和NIO2。

在写这篇文章之前,我读了这篇
Choosing tomcat connectors并看了Apache Tomcat Connector Selection

简而言之,我理解这一点:


NIO-在这种情况下,我们有选择器,该选择器在一个线程中按顺序轮询通道。作为每次轮询的结果,选择器
返回准备进行I / O操作的通道的标识符。
应用程序的任务是绕过循环中的所有标识符
并执行相应的操作。
APR-与NIO相同,但使用本机代码编写。
NIO2(AIO)-基于使用处理程序回调的交互。每种方法(例如write)都将作为参数
完成处理程序-接口的实现
java.nio.channels.CompletionHandler。方法启动请求的
在单独的线程上进行操作,并将控制权传递给下一个线程。什么时候
请求的操作已完全执行,触发了
开始操作,完成操作所给出的方法
处理程序。异步和NIO之间的主要区别是
AIO在多线程环境中工作:操作不是
在启动它们的流中进行表演。操作方式
NIO通过复用通道在单个线程中执行。


我有以下问题:
1)如果我不对tomcat使用TSL,那么在NIO之前,ARP有什么优势吗?我在预热后测试了我的应用程序,发现ARP处理请求的速度比NIO慢,偶尔会有请求处理的暂停。 JIT>本机代码?或者在某些条件下它可以运行得更快?
2)如果NIO2使用hadnler回调,是否意味着我需要分配更多线程?
3)NIO2是否比NIO快?因为乍看之下,NIO线程被迫显式轮询通道,而在NIO2是由操作系统而非应用程序本身启动的情况下。我的测试证实了这一点,但是每种情况都是个别的,所以我想知道细节。
4)所有连接器都具有sendfile功能。根据“ Choosing tomcat connectors”上方的资源,NIO2对此进行了仿真。这是什么意思?如果我使用Nginx提供静态文件,此功能是否有意义?

对我来说,最大的惊喜是所有连接器仍在使用阻塞操作。
官方文档confirms this
读取请求主体和写入响应标头,并且主体正在阻止操作。关于这个,我想在下面说。

接下来,我想了解所有线程如何在真实应用程序中工作。
例如,我有一个4核CPU,我选择了决定设置以下参数的NIO连接器:maxThread =“ 4”和maxConnection =“ 1000”

之后,我启动了VisualVM。这些是我感兴趣的主题:
http-nio-8080-exec [1-4],http-nio-8080-ClientPooler- [0-1]和NioBlockingSelector.BlockPoller

据我了解,ClientPollers管理读取请求行的非阻塞选择器,并等待保持活动的请求。 NioBlockingSelector.BlockPoller模拟对请求正文读取/响应写入的阻塞。但是为什么要使用阻塞操作?这是否与以下结论相矛盾:“慢速客户端不会使线程停顿”?
在所有这些酝酿的问题之后:
5)exec线程只处理了我的请求,还是执行从/到通道的读/写操作?我回答这个问题,是因为您可以想象这种情况,如果我们有10个传入请求,但是只有4个线程来处理请求。

我有以下假设:
a)同时,该Servlet处理4个请求,但是仍然可以从套接字读取剩余6个请求的数据(请求标头,正文)(因为我们在另一个线程中有工作池)。当前4个请求完成时,其余6个请求将已被读取所有数据,并且可以立即进行处理(按队列顺序)。因此,I / O不会空闲。 (但不能解决NioBlockingSelector中的阻塞操作问题)
b)Servlet处理4个请求,但是我们无法从网络缓冲区读取其他6个请求的数据,仅建立连接。事实证明,执行线程也执行任何读取操作,因此阻止了这些线程处理servlet中的“用户代码”。在这种情况下,我考虑异步servlet。我将线程池的大小确定为10。现在可以在控制器中定义如下长查询:

@GetMapping
public Callable<String> longRunningRequest(){
    return () -> {
        // long method
        return "someView";
    };
}


因此,现在exec线程始终可以读取/写入数据。
但是,如果我只是在连接器配置中定义maxThreads = 4 + 10,会有什么区别?对于异步servlet,似乎我们只是简单地将一个线程交换到另一个线程。如果对于短请求我使用exec线程,对长时间运行的请求使用带有专用线程池的异步servlet,则可能会有好处?

最佳答案

一个问题有很多问题。

简要地:


  如果我不在Tomcat上使用TLS,在NIO之前APR有什么优势吗?


没有。与NIO相比,APR的CPU占用率似乎略低,但事实并非如此。


  如果NIO2使用处理程序回调,这是否意味着我需要分配更多线程?


否。您对NIO和NIO2感到困惑。从应用程序的角度来看,所有这些事情都以相同的方式工作。 NIO不会“顺序轮询所有通道”。选择器线程实质上发出select(2)并等待OS通知线程该线程已准备好服务一个或多个通道。 NIO2的工作方式相同。


  NIO2比NIO快吗?


不必要。从理论上讲,NIO2应该比NIO“更好”,但是(a)它在目前的形式(2018年2月)上似乎并没有表现出更好的性能或效率,并且(b)比任何其他连接器都新得多,因此可能并非在所有情况下都100%可靠。 YMMV。


  所有连接器都具有sendfile功能,但是[NIO2]对此进行了仿真。


NIO2出于各种原因不能使用“ true” sendfile(2)。客户可以调用它,但是Java将完成工作,而不是OS。


  如果我[已经]使用Nginx提供静态文件,[使用sendfile]是否有意义?


否。如果您有提供静态文件的反向代理,则只会降低将这些请求代理到Tomcat,然后使用sendfile(模拟或不模拟)将数据返回给客户端的性能。


  对我来说,最大的惊喜是所有连接器仍在使用阻塞操作。


为什么那会让你感到惊讶? Servlet API建立在阻塞API(例如java.io.InputStreamjava.io.OutputStream等)上。如果您想要非阻塞行为,则需要使用Websocket。

07-26 07:29