【问题标题】:JDBC calls wrapped in Scala FutureScala Future 中包装的 JDBC 调用
【发布时间】:2019-09-29 05:58:13
【问题描述】:

我正在编写一个 Akka HTTP Web API,它使用 OJDBC 连接到一个 Oracle 数据库实例。

据我所知,没有用于连接数据库的异步 JDBC API,也没有回调实现,因此,必须阻塞进程线程才能完成调用。

我的问题是:拥有 Akka HTTP 自然允许使用 Scala Future 处理请求,将数据库调用简单地包装到 Scala Future 中是否是个好主意?底层线程在等待数据库响应时是否空闲?

【问题讨论】:

    标签: scala jdbc concurrency future


    【解决方案1】:

    底层线程在等待数据库响应时是否空闲? 是的,线程被阻塞直到 JDBC 调用完成。这不是一件好事,但在adba 准备好之前,可能没有更好的选择。

    使用 Future 来像 JDBC 调用一样阻塞 IO 是一种常见的模式。不过有一些事情需要考虑。 github 上有一篇关于该主题的精彩文章。

    总结文章中描述的一些要点:

    • 将您的阻塞调用封装在 blocking 块中,如下所示:

      def fetchUser(id: Long): Future[User]  = Future {
         blocking { //mark this operation as blocking
            ...
            preparedStatement.execute()
            ...
         }
      } 
      
    • 您不应该将scala.concurrent.ExecutionContext.Implicits.global 用于执行任何阻塞的期货,因为您可能会饿死线程池。你应该为你的阻塞操作创建一个单独的线程池:

      object BlockingIOExecutionContext {
          implicit val ec: ExecutionContextExecutor = ExecutionContext.fromExecutor(
             Executors.newCachedThreadPool()
          ) // create seperate thread pool for our blocking operations
      }
      

    对你来说最好的选择就是使用某种成熟的 Scala 框架,为你做这些事情,比如 slickdoobie

    【讨论】:

    • 非常感谢您的回答!我只有一个关于线程池饥饿的问题。在 Akka 中,ExecutionContext 实例由 Actor 系统给出,如下所示:implicit val system: ActorSystem = ActorSystem("my-system") implicit val ec: ExecutionContextExecutor = system.dispatcher ExecutionContext.Implicits.global 的相同建议对actor系统执行上下文是否正确?
    • 看看这个article。它描述了如何使用单独的调度程序进行阻塞操作。
    • 您不需要单独的调度程序。只需将您的默认调度程序配置为具有无限线程(或您期望合理的任何限制)。
    • 我认为在Future 中使用blocking 可以防止线程池饥饿,因为它会导致底层执行程序产生更多线程。请参阅docs.scala-lang.org/overviews/core/futures.html:“仅当每个阻塞调用都包含在阻塞调用中时,并发阻塞计算的数量才能超过并行度……否则,线程池可能会被饿死”
    • 我想这里的缺点是,如果你为 8 个线程固定了线程池,并且首先有 8 个 JDBC 调用,然后 CPU 繁重的任务来了,他们需要等到调用完成才能开始工作。另一种解决方法是没有问题的,因为使用blocking 会增加线程超过限制。
    猜你喜欢
    • 1970-01-01
    • 2020-05-13
    • 2015-10-09
    • 2017-02-27
    • 2015-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    相关资源
    最近更新 更多