Ratpack 为阻塞操作创建无限(*)线程池。它在以下位置创建DefaultExecController
:
public DefaultExecController(int numThreads) {
this.numThreads = numThreads;
this.eventLoopGroup = ChannelImplDetector.eventLoopGroup(numThreads, new ExecControllerBindingThreadFactory(true, "ratpack-compute", Thread.MAX_PRIORITY));
this.blockingExecutor = Executors.newCachedThreadPool(new ExecControllerBindingThreadFactory(false, "ratpack-blocking", Thread.NORM_PRIORITY));
}
在这个池中创建的线程不会在阻塞操作完成后立即被杀死——它们在池中空闲并等待下一个工作要做。其背后的主要原因是保持线程处于空闲状态比在需要时产生新线程更便宜。这就是为什么当你模拟 2500 个并发用户调用和执行阻塞操作的端点时,你会看到这个池中有 2500 个线程。创建的缓存线程池使用以下ThreadPoolExecutor
对象:
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), threadFactory);
}
其中2147483647
是最大池大小,60L
是以秒表示的 TTL。这意味着执行器服务将保留这些线程 60 秒,并且当它们在 60 秒后没有被重新使用时,它将清理它们。
这种情况下的高 CPU 实际上是意料之中的。2500 个线程正在使用 CPU 的几个内核。这也很重要 - 您的 SQL 数据库在哪里运行?如果你在同一台机器上运行它,那么你的 CPU 就更难了。如果您在阻塞线程池上运行的操作消耗大量 CPU 时间,那么您必须优化这些阻塞操作。Ratpack 的强大功能来自异步和非阻塞架构 - 处理程序使用ratpack-compute
线程池并将所有阻塞操作委托给,ratpack-blocking
因此您的应用程序不会被阻塞并且可以处理大量请求。
(*) 在这种情况下,unlimited 表示受可用内存限制,或者如果您有足够的内存,则受2147483647
线程限制(此值用于ExecutorService.newCachedThreadPool(factory)
)。