有几种方法可以处理阻塞调用。我不能说哪个是最好的,因为它肯定取决于特定的用例,并且需要大量的基准测试。
默认情况下,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
去完成。第三种选择是使用Actor
处理数据库调用,但这更复杂并且超出了这个问题的范围。
底线是,yes,使用更大的线程池或不同的ExecutionContext
用于阻止呼叫,如您never如果可以的话,想在默认线程池中阻塞。
这一切都在播放线程池文档 http://www.playframework.com/documentation/2.3.x/ThreadPools。 (最新版本)