有几种方法可以处理阻塞调用。我不能说哪个是最好的,因为它肯定取决于特定的用例,并且需要大量的基准测试。
默认情况下,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
来阻塞调用,因为如果你能提供帮助,你永远不想阻塞在默认线程池中。
这在线程池的播放文档中都有概述。(最新版本)