【问题标题】:Play's execution contexts vs scala globalPlay 的执行上下文 vs scala global
【发布时间】:2015-08-28 14:20:27
【问题描述】:

执行上下文是如何从

import scala.concurrent.ExecutionContext.Implicits.global

不同于 Play 的执行上下文:

import play.core.Execution.Implicits.{internalContext, defaultContext}

【问题讨论】:

    标签: scala playframework


    【解决方案1】:

    它们非常不同。

    在 Play 2.3.x 及之前的版本中,play.core.Execution.Implicits.internalContext 是具有固定大小限制的 ForkJoinPool,由 Play 内部使用。您永远不应该将它用于您的应用程序代码。来自文档:

    Play 内部线程池 - 这是 Play 在内部使用的。此线程池中的线程不应执行任何应用程序代码,并且不应在此线程池中执行任何阻塞。它的大小可以通过在application.conf中设置internal-threadpool-size来配置,默认为可用处理器的数量。

    相反,您将使用play.api.libs.concurrent.Execution.Implicits.defaultContext,它使用ActorSystem

    在 2.4.x 中,它们都使用相同的 ActorSystem。这意味着 Akka 将在其自己的线程池中分配工作,但以您不可见的方式(配置除外)。多个 Akka actor 可以共享同一个线程。

    scala.concurrent.ExecutionContext.Implicits.global 是在 Scala 标准库中定义的 ExecutionContext。这是一个特殊的ForkJoinPool,它使用blocking 方法来处理潜在的阻塞代码,以便在池中生成新线程。你真的不应该在 Play 应用程序中使用它,因为 Play 无法控制它。如果您不小心,它还可能产生 大量 线程并使用大量内存。

    我在this answer 中写了更多关于scala.concurrent.ExecutionContext.Implicits.global 的文章。

    【讨论】:

      【解决方案2】:

      它们是相同的并且指向你的底层actor系统的默认调度器 Play 或 Akka 或组合应用程序。

      ##默认播放的上下文

      play.api.libs.concurrent.Execution.Implicits.defaultContext
      

      ##Play 的内部上下文

      play.core.Execution.Implicits.internalContext
      

      ##Guice 的 EC 注入

      class ClassA @Inject()(config: Configuration)
                                 (implicit ec: ExecutionContext) {
      ...
      }
      

      但这是不同的

      scala.concurrent.ExecutionContext.Implicits.global
      

      还有 DB 驱动程序,例如如果你使用 slick,可能会想出自己的 Execution Context。总之,


      最佳实践:

      • 在使用 play 或 akka 框架时不要使用scala.concurrent.ExecutionContext.Implicits.global,这样在高负载时可能会使用比最佳线程更多的线程,从而导致性能下降。
      • 别害怕!在任何地方都可以尽可能多地使用默认调度程序,除非您执行一些阻塞任务,例如监听网络连接,或者显式地从 db 读取,这让您“当前三”等待结果。
      • 从默认执行程序开始,如果您发现 Play / Akka 在高负载期间响应不佳,请切换到新的线程池以执行耗时的计算任务。
        • 耗时较长的计算任务通常不会被视为阻塞。例如遍历内存中的自动完成树。但是,当您有时间进行计算任务时,如果您想让控制结构保持正常运行,您可能会认为它们是阻塞的。
        • 当您将计算任务视为非阻塞时可能发生的坏事是,当所有线程都在高负载计算时,播放和 Akka 消息调度程序将暂停。单独调度程序的优点是队列处理器不会饿死。使用单独调度程序的缺点是您可能会分配更多优化的线程,并且您的整体性能会降低。
        • 区别在于高负载服务器,小项目不用担心,使用默认
      • 当您的应用程序中没有运行其他执行程序时,请使用 scala.concurrent.ExecutionContext.Implicits.global。那就不用担心这是安全的。
      • 创建 Futures 后,使用默认池,这是最安全的方法,除非您确定 future 被阻塞。然后尽可能使用单独的池或使用阻塞结构。
      • 创建一个单独的线程池一次
        • Await 未来
        • 你调用 Thread.sleep
        • 您正在读取流/套接字/http 调用
        • 使用阻塞驱动程序手动查询 db,(通常 slick 是安全的)
        • 安排一个任务在 10 秒内运行
        • 安排任务每秒运行一次
      • 对于未来的映射/恢复操作,使用默认执行器,通常这是安全的
      • 使用默认调度程序可以安全地处理异常
      • PlayAkka 中始终使用 Akka 调度程序,在application.conf 中定义一个新的调度程序是一种很好的方式

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-06
        • 1970-01-01
        • 2021-03-24
        • 1970-01-01
        • 1970-01-01
        • 2012-08-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多