【问题标题】:Tomcat and acceptCount not workingTomcat 和 acceptCount 不工作
【发布时间】:2018-10-09 07:25:24
【问题描述】:

我正在使用 tomcat 8 作为 Spring Boot 项目的一部分,我的 acceptCount 设置似乎不起作用。我的服务器不仅接受 300 个连接,还接受了我扔给它的近 1000 个连接,当然同时处理的连接不超过 200 个。

Tomcat 文档在 acceptCount 上似乎很清楚:“当所有可能的请求处理线程都在使用时,传入连接请求的最大队列长度。队列已满时收到的任何请求都将被拒绝。”但显然这不会发生。

当然还有另一个设置,maxConnections,它的文档说:“请注意,一旦达到限制,操作系统可能仍会根据 acceptCount 设置接受连接” - 但仅此注释并不意味着 acceptCount是“基于 maxConnections,而不是 maxThreads”(就像 https://coderanch.com/t/647733/application-servers/Tomcat-BIO-connector-configurations 的 topicstarter 认为的那样)。它只是意味着它在两种情况下都会产生影响:当所有处理线程都忙时,以及当所有可用连接都用尽时。 (即使那个人是正确的,那也意味着定义 acceptCount 时 Tomcat 文档是完全错误的......)

那为什么会被忽略呢?我发现了一些讨论,人们据称声称 acceptCount 对他们不起作用,但没有找到实际的讨论:(即使相反,我也能找到一些抱怨 Tomcat 在 300 个连接后如何阻塞(这完全是默认设置) maxThreads + acceptCount)。所以我可以看到它对某些人有效,并且我被要求相信“也许”对某些人不起作用。对我来说也不是。我是否应该相信 Tomcat 手册在某种意义上是错误的这个选项并不总是得到尊重?

【问题讨论】:

  • 顺便说一句,我如何检测到连接已被接受。我使用 curl 以详细模式发送请求(并行执行的许多 curl 实例),并计算其中有多少报告已建立连接。他们中的大多数甚至报告发送了一个请求,但我们当然不能确定他们是否意味着刚刚开始发送,或者实际上完成了将请求正文推送到一个 socked 中。
  • 发布您的有效配置,并说明您的测试方法。
  • 我在下面的 cmets 中提供了更多详细信息。

标签: tomcat spring-boot tomcat8


【解决方案1】:

acceptCount 用于 NIO 作为第二个参数(积压)传递给ServerSocket.bind(SocketAddress,int)。该方法的 Javadoc 说:

积压参数是请求的最大数量 套接字上的挂起连接。它的确切语义是实现 具体的。特别是,一个实现可以强加一个最大长度 或者可以选择完全忽略该参数。

看起来它在你的 JVM 中被忽略了。

【讨论】:

  • 很有可能,谢谢。但这表明参考手册不完整,因为它没有说可以忽略该参数。
  • 重新阅读此答案中的引文,特别是最后 7 个单词。
  • 我为什么要这样做?你以为我不明白这个 Javadoc 说参数可以忽略吗?嗯,我愿意。但是拥有正确的 Javadoc 并不意味着 reference manual(我引用的)也不一定是正确的。
  • 这就是我一直在寻找的。​​span>
【解决方案2】:

所有这些设置之间存在复杂的关系,并且您选择的连接器(例如 BIO、NIO、APR 等)会进一步复杂化。

BIO 连接器基本上已经死了……它在 Tomcat 8.0.x 之后不存在。它不能接受比线程池一次可以处理的更多的连接。因此,BIO 连接器本质上使maxConnections == maxThreads。因此,对于一个 BIO 连接器,服务器愿意接受的连接数应该是maxConnections + acceptCount,但maxConnections 仅限于一些相对较小的数量。

在 TCP/IP 意义上,其他更复杂的连接器允许每个线程接受一个以上的连接maxConnections 的默认值接近 10k(因具体情况而异) type),和线程池大小无关,所以服务器愿意接受的连接数是maxConnections + acceptCount

非 BIO 连接器可以接受这么多连接的原因是因为两种状态都不需要活动的请求处理线程(等待下一个 HTTP-keepalive-request - 使用 read() 并等待下一个连接 - 使用 accept()) 由单独的线程完成,允许请求处理线程尽快返回池中以便为其他请求提供服务。

如果您使用 BIO,我预计连接会失败,例如300 个连接(您没有发布您的配置,因此无法说出实际数字是多少)但对于 NIO,我希望它在 10k 连接以北。

对于测试,重要的是您实际上不对连接进行任何操作,以便正确计算它们。基本上,你需要这样做:

foreach(i from 0..10301)
    conn[i] = connect('host:port')

您应该会发现有一些i 服务器不接受连接。如果您连接并发出GET /,那么服务器将响应并且连接请求处理线程将返回各自的池中。

** 阅读一些 cmets 后更新**

我希望能够同时处理 200 个请求,但 Tomcat 很乐意在内部将另外 800 个请求排队。 acceptQueue 中的另外 200 个正在由操作系统的 TCP/IP 堆栈而不是 Tomcat/Java 排队

假设客户端的读取超时无限,我希望您能够启动 1201 个 curl 实例,其中门中的前 200 个实例立即获得一个线程(并休眠),接下来的 800 个进入队列Tomcat/Java,接下来的 200 个在 TCP/IP 堆栈中排队,实例 #1201 获得“连接被拒绝”响应。

一旦完成第一组 200 个请求,Tomcat 将处理另一批 200 个请求,TCP/IP 队列中的 200 个连接将从该队列移动到 Tomcat/Java 队列,20 秒后第二组200 个请求将完成。这将重复,直到所有 1200 个初始请求都向其客户端返回响应。 1200 个请求中只有 1 个会被拒绝。

【讨论】:

  • 嗯,我说的是 NIO 连接器,我大致了解 NIO 的工作方式,但感谢您提供的解释。问题是,“我希望它在 10k 连接以北”是可以的,但是手册中有一些定义,让我重复一下我的引用:acceptCount="当所有可能的请求处理线程都在时,传入连接请求的最大队列长度正在使用。”即使服务器能够提供 10000 个连接,它一次也只能提供 maxThreads=200 个请求;添加 acceptCount=100 并期望它在第 300 个传入连接时阻塞。
  • “如果你连接并发出 GET / 然后服务器会响应” - 我这样做;在响应之前,我还在服务器端执行 Thread.sleep(20000),阻塞处理线程 20 秒。而且,我用 for + curl + & 发出了 1000 个并行请求,其中几乎都成功连接并最终得到了服务。
  • Christopher,但是为什么您希望 tomcat 在最初的 200 个连接请求之后再排队 800 个连接请求?这个数字是从哪里来的?此外,这与手册相矛盾:acceptCount=100 是在使用所有可能的请求处理线程时传入连接请求的最大队列长度。据此,tomcat 或其他任何人(包括 tcp 堆栈)不应将任何超过 maxThreads+acceptCount=300 的连接排队,我不想再重复一遍:)
  • 我完全同意您对 什么 正在发生的理解(200 个然后 200 个等),我只是不明白 为什么 正在发生。根据 Tomcat 文档,它不应该。
  • 好吧,我刚才又引用了一遍,maxThreads+acceptCount=300 应该会阻止tomcat 接受第300 个连接。然而你认为“Tomcat 会很高兴地在内部排队另外 800 个请求”并没有错。
猜你喜欢
  • 2021-04-23
  • 2011-12-30
  • 2019-04-10
  • 1970-01-01
  • 2015-04-22
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
相关资源
最近更新 更多