如何使用适当的线程池调整Play Framework应用程序?

时间:2014-10-03 06:28:01

标签: scala playframework-2.3

我正在使用Play Framework(Scala)2.3版。来自文档:

  

通过将同步IO包装在Future中,您无法神奇地将同步IO变为异步。如果您无法更改应用程序的体系结构以避免阻塞操作,那么在某些时候必须执行操作,并且该线程将阻塞。因此,除了将操作封装在Future中之外,还需要将其配置为在单独的执行上下文中运行,该上下文已配置有足够的线程来处理预期的并发性。

这让我对如何调整我的webapp感到困惑。具体来说,由于我的应用程序有大量的阻塞调用:混合使用JDBC调用,以及使用阻塞SDK调用第三方服务,配置执行上下文和确定要提供的线程数的策略是什么?我需要一个单独的执行上下文吗?为什么我不能简单地将默认池配置为具有足够数量的线程(如果我这样做,为什么我还需要将调用包装在Future中?)?

我知道这最终将取决于我的应用程序的细节,但我正在寻找关于策略和方法的一些指导。游戏文档鼓励在任何地方使用非阻塞操作,但实际上典型的网络应用程序命中sql数据库有许多阻止调用,我从阅读这类应用程序的文档中得到的印象将使用默认配置进行最佳操作。

2 个答案:

答案 0 :(得分:6)

  

[...]配置执行上下文的策略是什么?   确定要提供的线程数

嗯,这是一个棘手的部分,取决于您的个人要求。

  • 首先,您可能应该从docs(纯异步,高度同步或许多特定线程池)中选择基本配置文件。
  • 第二步是通过对应用程序进行概要分析和基准测试来微调您的设置
  

我是否需要单独的执行上下文?

不一定。但是,如果要一次触发所有阻塞IO调用而不是顺序触发,则使用单独的执行上下文是有意义的(因此数据库调用B不必等到数据库调用A完成)。

  

为什么我不能简单地将默认池配置为足够   线程数量(如果我这样做,为什么我还需要换行   未来的电话?)?

您可以查看docs

play {
  akka {
    akka.loggers = ["akka.event.slf4j.Slf4jLogger"]
    loglevel = WARNING
    actor {
      default-dispatcher = {
        fork-join-executor {
          parallelism-min = 300
          parallelism-max = 300
        }
      }
    }
  }
}

通过这种方法,您基本上将Play变为每个请求的一个线程模型。这不是Play背后的想法,但如果您正在进行大量阻止IO调用,那么这是最简单的方法。在这种情况下,您不需要在Future中包装数据库调用。

简而言之,你基本上有三种方法可以去:

  1. 仅使用其API调用是非阻塞和异步的(IO-)技术。这允许您使用适合Play
  2. 性质的小线程池/默认执行上下文
  3. 通过大幅增加默认执行上下文,将Play转换为每个请求一个线程的框架。不需要期货,只需像往常一样调用您的阻止数据库
  4. 为阻止IO调用创建特定的执行上下文,并对您正在执行的操作进行细粒度控制

答案 1 :(得分:3)

首先,在深入了解并重构您的应用之前,您应该确定这对您来说是否真的有问题。运行一些基准测试(加特林非常棒)并使用类似JProfiler的东西做一些配置文件。如果你能忍受目前的表现那么快乐的日子。

理想的情况是使用一个反应式驱动程序,它可以让你回到未来,然后一直传递回你的控制器。不幸的是,异步仍然是Open ticket for slick。使用PlayWS library可以使用REST API进行交互,但如果您必须通过第三方提供的库,那么您就会被卡住。

因此,假设这些都不可行并且您确实需要提高性能,那么问题是Play的建议会带来什么好处?我认为他们在这里得到的是将线程划分为阻塞和可以使用异步技术的线程是有用的。

例如,如果只有一部分请求是长的并且阻塞然后使用单个线程池,那么您将冒险将所有线程用于阻塞操作。然后,您的控制器将无法处理任何新请求,无论该请求是否需要调用阻止服务。如果你可以分配足够多的线程,那就没有问题了。

另一方面,如果您达到了线程限制,那么通过使用两个池,您可以保持快速,无阻塞的请求。您将在控制器中有一个池服务请求并调用返回期货的服务。这些期货中的一些实际上将使用单独的线程池执行工作,但仅用于阻塞操作。如果您的应用程序的任何部分可能被激活,那么您的控制器可以利用这一点,同时将控制器与阻塞操作隔离。