Play Framework 2.X和阻止数据库调用

时间:2014-07-06 01:22:21

标签: scala asynchronous playframework nonblocking playframework-2.3

我有点困惑。

来自documentation

  

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

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

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

我遇到this post,但我对给定的答案不满意 实际上,如果我让数据库在默认线程池中调用阻塞,那么在此期间它是否可能阻止处理不依赖于数据库的其他用户请求?

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

1 个答案:

答案 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中概述了这一点。 (最新版本)