如何确定 Tomcat 中 maxSpare、minSpare 和 maxThreads、acceptCount 等的最佳数量?是否存在现有的最佳实践?
我确实理解这需要基于硬件(例如每个内核),并且只能作为对特定硬件进行进一步性能测试和优化的基础。
如何确定 Tomcat 中 maxSpare、minSpare 和 maxThreads、acceptCount 等的最佳数量?是否存在现有的最佳实践?
我确实理解这需要基于硬件(例如每个内核),并且只能作为对特定硬件进行进一步性能测试和优化的基础。
“多少线程问题”是一个相当大而复杂的问题,不能用简单的经验法则来回答。
考虑到您拥有的内核数量对于倾向于消耗大量 CPU 的多线程应用程序(例如数字运算等)很有用。对于 Web 应用来说,这种情况很少见,它通常不是被 CPU 占用,而是被其他因素占用。
一个常见的限制是您与其他外部系统之间的延迟,尤其是您的数据库。每次请求到达时,它可能会多次查询数据库,这意味着通过 JDBC 连接流式传输一些字节,然后等待这些字节到达数据库(即使它在本地主机上仍然有一个小延迟) ,然后等待数据库考虑我们的请求,然后等待数据库处理它(数据库本身将等待磁盘寻找某个区域)等等......
在这段时间内,线程处于空闲状态,因此另一个线程可以轻松地使用该 CPU 资源来做一些有用的事情。40% 到 80% 的时间花在等待数据库响应上是很常见的。
同样的情况也发生在连接的另一端。当您的线程将其输出写入浏览器时,CLIENT 连接的速度可能会使您的线程空闲等待浏览器确认已收到某个数据包。(几年前这是一个相当大的问题,最近的内核和 JVM 使用更大的缓冲区来防止您的线程以这种方式空闲,但是在您的 Web 应用程序服务器之前的反向代理,即使只是一个 httpd,对于避免人们真的很有用互联网连接不良以充当 DDOS 攻击 :) )
考虑到这些因素,线程数通常应该比您拥有的内核数多得多。即使在简单的双核或四核服务器上,您至少应该配置几十个线程。
那么,是什么限制了您可以配置的线程数?
首先,每个线程(用于)消耗大量资源。每个线程都有一个堆栈,它消耗 RAM。此外,每个线程实际上会在堆上分配一些东西来完成它的工作,再次消耗 RAM,并且线程之间的切换(上下文切换)对于 JVM/OS 内核来说是相当繁重的。
这使得很难“顺利”运行具有数千个线程的服务器。
鉴于这张图片,有许多技术(主要是:尝试、失败、调整、重试)来确定您的应用程序需要多少线程:
1)尝试了解您的线程花费时间的地方。有许多很好的工具,但即使是 jvisualvm 分析器也可以是一个很好的工具,或者是一个生成摘要计时统计信息的跟踪方面。他们花在等待外部事物上的时间越多,您就越能在空闲时间产生更多线程来使用 CPU。
2) 确定您的 RAM 使用情况。鉴于 JVM 将使用一定数量的内存(最显着的是 permgen 空间,通常高达 100 兆字节,jvisualvm 会再次说明)与您使用的线程数无关,请尝试使用一个线程运行,然后使用 10 个线程,然后使用一百,同时用 jmeter 或其他什么来强调应用程序,看看堆使用量将如何增长。这可能构成硬性限制。
3) 尝试确定目标。每个用户请求都需要一个线程来处理。如果您的平均响应时间是每次“获取”200 毫秒(最好不要考虑加载图像、CSS 和其他静态资源),那么每个线程每秒可以处理 4/5 个页面。如果每个用户预计每 3/4 秒“点击”一次(取决于它是浏览器游戏还是有很多长文本的网站?),那么一个线程将“为 20 个并发用户提供服务”,无论这意味着什么。如果在高峰时段您有 500 个单用户在 1 分钟内访问您的网站,那么您需要足够的线程来处理它。
4) 碰撞测试上限。使用 jmeter,在备用虚拟机上配置具有大量线程的服务器,看看当您超过某个限制时响应时间会如何变差。不仅仅是硬件,底层操作系统的线程实现在这里也很重要,但无论如何它都会达到 CPU 花费更多时间试图找出运行哪个线程而不是实际运行它的地步,而且这个数字并不是那么令人难以置信高的。
5)考虑线程将如何影响其他组件。每个线程可能会使用一个(或多个)与数据库的连接,数据库是否能够处理 50/100/500 个并发连接?即使您使用的是 nosql 服务器的分片集群,服务器场是否在这些机器之间提供足够的带宽?还有什么会在与 web-app 服务器相同的机器上运行?阿纳什 httpd?乌贼?数据库本身?数据库的本地缓存代理,如 mongos 或 memcached?
我见过只有 4 个线程 + 4 个备用线程的生产系统,因为该服务器所做的工作只是调整图像大小,因此它几乎 100% 占用 CPU,而其他系统则配置在或多或少相同的硬件上几百个线程,因为 webapp 正在对外部系统进行大量 SOAP 调用,并且大部分时间都在等待答案。
一旦你确定了大约。最适合您的 webapp 的最小和最大线程,然后我通常这样配置它:
1)基于对RAM、其他外部资源和上下文切换实验的限制,绝对不能达到最大值。因此,使用 maxThreads 将其限制为该数字的大约一半或 3/4。
2) 如果应用程序相当快(例如,它公开了通常发送响应为几毫秒的 REST Web 服务),那么您可以配置一个较大的 acceptCount,最多与 maxThreads 数量相同。如果您的 Web 应用程序服务器前面有一个负载均衡器,请设置一个小的 acceptCount,负载均衡器最好能看到未接受的请求并切换到另一台服务器,而不是让用户等待已经很忙的服务器。
3) 由于启动线程(仍然)被认为是一项繁重的操作,因此当高峰时间到来时,请使用 minSpareThreads 准备好几个线程。这又取决于您期望的负载类型。设置 minSpareThreads、maxSpareThreads 和 maxThreads 甚至是合理的,以便始终准备好确切数量的线程,从不回收,并且性能是可预测的。如果您在专用机器上运行 tomcat,则可以提高 minSpareThreads 和 maxSpareThreads 而不会占用其他进程,否则将它们调低,因为线程是与大多数操作系统上运行的其余进程共享的资源。