6

我有点困惑。

文档中:

Play 默认线程池 - 这是默认线程池,其中执行 Play 框架中的所有应用程序代码,不包括一些迭代代码。它是一个 Akka 调度器,可以通过配置 Akka 来配置,如下所述。默认情况下,每个处理器有一个线程。

为了让默认线程池处理其他用户请求 ,将阻塞数据库调用包装在 a 中是否会带来好处Future,该调用Future本身由async控制器包装(返回它),以便让默认线程池处理?

它只会将阻塞代码移动到另一个线程中(来自专用的 ExecutionContext),但不会Action阻塞。

我遇到了这篇文章,但我对给定的答案不满意。
确实,如果我让数据库调用阻塞在默认线程池中,它不会同时阻止处理其他不依赖数据库的用户请求吗?

注意:我的数据库 (Neo4j) 没有异步驱动程序。

4

1 回答 1

10

有几种方法可以处理阻塞调用。我不能说哪个是最好的,因为它肯定取决于特定的用例,并且需要大量的基准测试。

默认情况下,Play 使用每个 cpu 核心一个线程的线程池处理请求。因此,例如,如果您在四核 cpu 上运行 Play 应用程序,那么如果它们使用对数据库的阻塞调用,它将只能处理 4 个并发请求。所以是的,所有其他传入请求都必须等到其中一个线程被释放。

最简单的解决方案是增加 Play 在默认线程池(在 application.conf 中)中用于处理请求的线程数:

play {
   akka {
     akka.loggers = ["akka.event.slf4j.Slf4jLogger"]
     loglevel = WARNING
     actor {
        default-dispatcher = {
           fork-join-executor {
             parallelism-min = 300
             parallelism-max = 300
           }
        }
     }
   }
}

下一个选项是您在问题中提到的选项 - 将阻塞数据库调用卸载到另一个ExecutionContext. 您可以在 application.conf 中配置一个单独的线程池,如下所示:

database-io {
    fork-join-executor {
       parallelism-factor = 10.0
    }
}

这将在名为 的池中为每个 cpu 核心创建 10 个线程database-io,并且可以在 Play 中访问,如下所示:

val dbExecutor: ExecutionContext = Akka.system.dispatchers.lookup("database-io")

val something = Future(someBlockingCallToDb())(dbExecutor)

这将允许默认线程池在等待Future完成时处理更多请求。第三种选择是使用 anActor来处理数据库调用,但这更复杂,超出了这个问题的范围。

底线是,的,使用更大的线程池或不同的线程池ExecutionContext来阻塞调用,因为如果你能提供帮助,你永远不想阻塞在默认线程池中。

这在线程池的播放文档中都有概述。(最新版本)

于 2014-07-06T01:54:13.913 回答