注意:我的I / O不强,所以在我的进一步推理中我可能是错的。
首先,我想了解tomcat连接器的工作原理。 Tomcat 9有3个连接器: NIO , APR 和 NIO2 。
在写这篇文章之前,我读过这篇文章 Choosing tomcat connectors并查看Apache Tomcat Connector Selection
简而言之,我理解这一点:
我有以下问题:
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个请求(因为我们在另一个线程中有工作的poolers )。当前4个请求完成时,剩余的6个已经读取了所有数据,并且可以立即处理(按队列的顺序)。因此,I / O不会空闲。 (但未解决 NioBlockingSelector 中的阻止操作问题)
b )Servlet处理4个请求,但我们无法从网络缓冲区读取其他6个请求的数据,只建立连接。事实证明 exec线程 也执行任何读取操作,因此这些线程被阻止处理"用户代码"在servlet中。在这种情况下,我想到 async servlet 。我确定了一个10的线程池大小。现在控制器中的长查询我可以这样定义:
@GetMapping
public Callable<String> longRunningRequest(){
return () -> {
// long method
return "someView";
};
}
因此,现在 exec线程 随时可用于读/写数据。 但是,如果我只是在连接器配置中定义maxThreads = 4 + 10,有什么区别?在异步servlet的情况下,似乎我们只是将一个线程交换到另一个线程。或者,对于我使用 exec thread 的简短请求可能会有好处,对于长时间运行的请求,可以使用 async servlet 使用专用线程池?
答案 0 :(得分:1)
对于一个问题,这是一个很多问题。
简言之:
如果我不在Tomcat上使用TLS,那么在NIO之前APR是否有任何优势?
无。 APR似乎比NIO的CPU密集程度略低,但并非如此。
如果NIO2使用处理程序回调,这是否意味着我需要分配更多线程?
没有。您对NIO和NIO2感到困惑。从应用程序的角度来看,所有这些都以相同的方式工作。 NIO没有“按顺序轮询所有频道”。选择器线程实质上发出select(2)
并等待操作系统通知线程一个或多个通道已准备好进行服务。 NIO2的工作方式有点相同。
NIO2比NIO更快吗?
不一定。从理论上讲,NIO2应该比NIO“更好”,但是(a)它目前的表现似乎没有任何更好或更高效(2018年2月)和(b)它比任何其他连接器都要新得多,因此在所有情况下可能都不是100%可靠。 YMMV。
所有连接器都有sendfile功能,[但] NIO2模拟了这一点。
由于各种原因,NIO2无法使用“true”sendfile(2)
。客户可以调用它,但Java将完成工作,而不是操作系统。
如果我[已经]使用Nginx来提供静态文件,那么[使用
sendfile
]是否有意义?
没有。如果您有一个反向代理服务静态文件,它只会降低性能,将这些请求代理到Tomcat,然后使用sendfile
(模拟或不模拟)将数据返回给客户端。
对我来说最大的惊喜是所有连接器仍在使用阻塞操作。
为什么要让你感到惊讶? servlet API基于阻止API(例如java.io.InputStream
,java.io.OutputStream
等)构建。如果您想要非阻塞行为,则需要使用Websocket。