具有异步servlet工作和可伸缩性的Tomcat连接器

时间:2018-02-17 02:02:21

标签: java multithreading sockets tomcat

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

首先,我想了解tomcat连接器的工作原理。 Tomcat 9有3个连接器: NIO APR NIO2

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

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

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

1 个答案:

答案 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.InputStreamjava.io.OutputStream等)构建。如果您想要非阻塞行为,则需要使用Websocket。