我们有一个用Ratpack 1.5.1编写的移动应用程序API服务器即将上线,我们目前正在分析该应用程序以捕获任何性能瓶颈。这个应用程序是由一个SQL数据库支持的,我们总是小心地使用Blocking类来运行查询。代码是用Kotlin编写的,我们编写了一些协程胶水代码来强制在Ratpack的阻塞线程上执行阻塞操作。
由于Ratpack的线程模型是独一无二的,我们希望确保这种情况是正常的:我们模拟了2500个并发用户的应用程序,我们的线程数量增加到400个(甚至一度达到600个),其中大部分是ratpack-blocking-x-yyy线程。
对CPU进行采样,我们在ratpack.exec.internal.DefaultExecController$ExecControllerBindingThreadFactory.lambda$newThread$0方法中花费了92%的时间,但这可能是采样的伪影。
那么,要问具体的问题:给定Ratpack的线程模型,高阻塞线程计数是正常的吗?我们是否应该担心上面提到的方法花费的高CPU时间?
发布于 2018-02-16 21:16:46
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);
}其中TTL是最大池大小,60L是以秒为单位的2147483647。这意味着executor服务将保留这些线程60秒,当它们在60秒后没有被重用时,它将清理它们。
在这种情况下,实际上应该使用高CPU。2500个线程正在使用CPU的几个核心。同样重要的是,您的SQL数据库在哪里运行?如果你在同一台机器上运行它,那么你的CPU会有更难的工作要做。如果您在阻塞线程池上运行的操作消耗了大量的CPU时间,那么您必须优化这些阻塞操作。ratpack-blocking的强大功能来自异步和非阻塞架构-处理程序使用Ratpack线程池,并将所有阻塞操作委托给Ratpack,因此您的应用程序不会被阻塞,并且可以处理大量请求。
(*) unlimited在这种情况下表示受可用内存的限制,或者如果您有足够的内存,则它受2147483647线程的限制(此值在ExecutorService.newCachedThreadPool(factory)中使用)。
发布于 2018-02-19 08:47:21
只是在西蒙的答案…的基础上
Ratpack本身并不限制任何操作。这实际上是由你决定的。您有一个选择,那就是使用https://ratpack.io/manual/current/api/ratpack/exec/Throttle.html来约束和排队对资源的访问。
https://stackoverflow.com/questions/48827194
复制相似问题