我有点困惑。
播放默认线程池 - 这是默认的线程池 Play框架中的所有应用程序代码都被执行,不包括一些 迭代代码。它是一个Akka调度程序,可以配置 配置Akka,如下所述。默认情况下,每个都有一个线程 处理器。
在Future
中包含阻塞数据库调用是否带来好处,Future
的调用本身由async
控制器包装(返回它),以便让处理其他用户请求的默认线程池?
它只会将阻塞代码移动到另一个线程(来自专用的ExecutionContext)中,但保持Action
未阻止。
我遇到this post,但我对给定的答案不满意 实际上,如果我让数据库在默认线程池中调用阻塞,那么在此期间它是否可能阻止处理不依赖于数据库的其他用户请求?
注意:我的数据库(Neo4j)没有异步驱动程序。
答案 0 :(得分: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核心创建一个名为database-io
的10个线程,并且可以在Play中访问,如下所示:
val dbExecutor: ExecutionContext = Akka.system.dispatchers.lookup("database-io")
val something = Future(someBlockingCallToDb())(dbExecutor)
这将允许默认线程池在等待Future
完成时处理更多请求。第三种选择是使用Actor
来处理数据库调用,但这更复杂,超出了这个问题的范围。
底线是,是,使用更大的线程池或不同的ExecutionContext
来阻止来电,因为从不想要阻止默认线程池,如果你能帮助它。
Play Documentation for Thread Pools中概述了这一点。 (最新版本)