为 Slick 使用单独的 ExecutionContext
Using a separate ExecutionContext for Slick
我正在使用带有 Slick 的 Play 2.5。关于这个主题的 docs 简单地说明一切都由 Slick and Play 的 Slick 模块管理。但是,此示例打印 Dispatcher[akka.actor.default-dispatcher]
:
class MyDbioImpl @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)(implicit ec: ExecutionContext)
with HasDatabaseConfigProvider[JdbcProfile] {
import profile.api._
def selectSomeStuff(): Future[MyResult] = db.run {
println(ec)
[...]
}
}
由于执行上下文打印在 db.run 中,似乎我所有的数据库访问也将在默认执行上下文中执行。
我找到 this answer 一个较旧的问题,当时它解决了这个问题。但是这个方案已经废弃了,建议使用依赖注入来获取应用上下文。当我尝试这样做时,我收到一条错误消息,指出 play.akka.actor.slick-context 不存在...
class MyDbioProvider @Inject()(actorSystem: ActorSystem,
protected val dbConfigProvider: DatabaseConfigProvider)
extends Provider[MyDbioImpl] {
override def get(): MyDbioImpl = {
val ec = actorSystem.dispatchers.lookup("play.akka.actor.slick-context")
new MyDbioImpl(dbConfigProvider)(ec)
}
}
编辑:
Slick 的执行上下文是在某个配置文件中定义的“正常”执行上下文吗?上下文切换发生在哪里?我假设“数据库世界”的入口点在 db.run
.
根据Slick:
Every Database contains an AsyncExecutor that manages the thread pool
for asynchronous execution of Database I/O Actions. Its size is the
main parameter to tune for the best performance of the Database
object. It should be set to the value that you would use for the size
of the connection pool in a traditional, blocking application (see
About Pool Sizing in the HikariCP documentation for further
information). When using Database.forConfig, the thread pool is
configured directly in the external configuration file together with
the connection parameters. If you use any other factory method to get
a Database, you can either use a default configuration or specify a
custom AsyncExecutor.
基本上它说你不需要创建一个独立的 ExecutionContext 因为 Slick 已经在内部隔离了一个线程池。您对 Slick 的任何调用都是非阻塞的,因此您应该使用默认的 ExecutionContext。
Slick 的实现可以在 BasicBackend.scala 文件中看到:runInContextSafe 方法。代码如下:
val promise = Promise[R]
val runnable = new Runnable {
override def run() = {
try {
promise.completeWith(runInContextInline(a, ctx, streaming, topLevel, stackLevel = 1))
} catch {
case NonFatal(ex) => promise.failure(ex)
}
}
}
DBIO.sameThreadExecutionContext.execute(runnable)
promise.future
如上所示,这里使用了Promise,然后利用其内部线程池快速执行其代码,返回Promise的Future对象。所以在执行Await.result/ready的时候,这里的Promise很可能已经被Slick的内部线程执行了,所以拿到结果就可以了,有可能在一个环境中执行Await.result/ready比如玩。共 non-blocking.
详情请参考Scala关于Future和Promise的文档:https://docs.scala-lang.org/overviews/core/futures.html
我正在使用带有 Slick 的 Play 2.5。关于这个主题的 docs 简单地说明一切都由 Slick and Play 的 Slick 模块管理。但是,此示例打印 Dispatcher[akka.actor.default-dispatcher]
:
class MyDbioImpl @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)(implicit ec: ExecutionContext)
with HasDatabaseConfigProvider[JdbcProfile] {
import profile.api._
def selectSomeStuff(): Future[MyResult] = db.run {
println(ec)
[...]
}
}
由于执行上下文打印在 db.run 中,似乎我所有的数据库访问也将在默认执行上下文中执行。
我找到 this answer 一个较旧的问题,当时它解决了这个问题。但是这个方案已经废弃了,建议使用依赖注入来获取应用上下文。当我尝试这样做时,我收到一条错误消息,指出 play.akka.actor.slick-context 不存在...
class MyDbioProvider @Inject()(actorSystem: ActorSystem,
protected val dbConfigProvider: DatabaseConfigProvider)
extends Provider[MyDbioImpl] {
override def get(): MyDbioImpl = {
val ec = actorSystem.dispatchers.lookup("play.akka.actor.slick-context")
new MyDbioImpl(dbConfigProvider)(ec)
}
}
编辑:
Slick 的执行上下文是在某个配置文件中定义的“正常”执行上下文吗?上下文切换发生在哪里?我假设“数据库世界”的入口点在 db.run
.
根据Slick:
Every Database contains an AsyncExecutor that manages the thread pool for asynchronous execution of Database I/O Actions. Its size is the main parameter to tune for the best performance of the Database object. It should be set to the value that you would use for the size of the connection pool in a traditional, blocking application (see About Pool Sizing in the HikariCP documentation for further information). When using Database.forConfig, the thread pool is configured directly in the external configuration file together with the connection parameters. If you use any other factory method to get a Database, you can either use a default configuration or specify a custom AsyncExecutor.
基本上它说你不需要创建一个独立的 ExecutionContext 因为 Slick 已经在内部隔离了一个线程池。您对 Slick 的任何调用都是非阻塞的,因此您应该使用默认的 ExecutionContext。
Slick 的实现可以在 BasicBackend.scala 文件中看到:runInContextSafe 方法。代码如下:
val promise = Promise[R]
val runnable = new Runnable {
override def run() = {
try {
promise.completeWith(runInContextInline(a, ctx, streaming, topLevel, stackLevel = 1))
} catch {
case NonFatal(ex) => promise.failure(ex)
}
}
}
DBIO.sameThreadExecutionContext.execute(runnable)
promise.future
如上所示,这里使用了Promise,然后利用其内部线程池快速执行其代码,返回Promise的Future对象。所以在执行Await.result/ready的时候,这里的Promise很可能已经被Slick的内部线程执行了,所以拿到结果就可以了,有可能在一个环境中执行Await.result/ready比如玩。共 non-blocking.
详情请参考Scala关于Future和Promise的文档:https://docs.scala-lang.org/overviews/core/futures.html