java - Tomcat连接器具有异步Servlet的工作能力和可伸缩性

标签 java multithreading sockets tomcat

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

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

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

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

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

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

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

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

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

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

    我有以下假设:
    servlet同时处理4个请求,但是仍然可以从套接字读取剩余6个请求的数据(请求 header ,正文)(因为我们在另一个线程中有工作池)。当前4个请求完成时,其余6个请求将已经读取所有数据,并且可以立即进行处理(按队列顺序)。因此,I/O不会空闲。 (但尚未解决 NioBlockingSelector 中的阻塞操作问题)
    Servlet处理4个请求,但是我们无法从网络缓冲区读取其他6个请求的数据,仅建立连接。事实证明, exec线程也执行任何读取操作,因此阻止了这些线程处理servlet中的“用户代码”。在这种情况下,我考虑了异​​步servlet 。我将线程池的大小标识为10。现在,我可以在 Controller 中定义如下长查询:
    @GetMapping
    public Callable<String> longRunningRequest(){
        return () -> {
            // long method
            return "someView";
        };
    }
    

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

    最佳答案

    一个问题有很多问题。

    简要地:

    Are there any advantages of APR before NIO if I don't use TLS on Tomcat?



    没有。 APR似乎比NIO的CPU占用率要少一些,但事实并非如此。

    If NIO2 uses handler callbacks, does this mean that i need to allocate more threads?



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

    Is NIO2 faster than NIO?



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

    All connectors have sendfile functional, [but] NIO2 emulates this.



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

    Does it make sense [to use sendfile], if i [already] use Nginx to serve static files?



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

    The biggest surprise for me was that all the connectors are still using blocking operations.



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

    关于java - Tomcat连接器具有异步Servlet的工作能力和可伸缩性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48837206/

    相关文章:

    c - TOR 上的基本 C Windows TCP 套接字,recv() 挂起?

    c# - 无法使用 RESP 在 Redis 服务器上执行命令

    java - 自动处理/忽略 Jython 中的 NameError

    c - 使用 opencv 在相机上线程化face_detection

    java - 检查编译时是否 someClass.class 派生自 anotherClass.class?

    java - 为什么 Autowiring 在线程中不起作用?

    java - FixedThreadPool 和 ThreadPoolTask​​Executor 有什么区别?

    c# - 接收 TCP 连接问题

    java - 在 java 中解析 XML 时没有工作 ID 属性

    java - RecyclerView.Adapter类查询