Play framework 2.0控制器/异步是如何工作的?

时间:2012-09-25 10:17:55

标签: scala playframework-2.0 akka evented-io

我最近搬到了Play framework 2.0,关于控制器实际上如何工作,我有一些问题。

play docs中提到:

  

由于Play 2.0的工作方式,动作代码必须尽可能快   可能的(即非阻塞)。

但是在another part of the docs

            /actions {
                router = round-robin
                nr-of-instances = 24
            }

        actions-dispatcher = {
            fork-join-executor {
                parallelism-factor = 1.0
                parallelism-max = 24
            }
        }

似乎有24名演员分配给控制器处理。我猜每个请求都会在请求的生命周期中分配其中一个actor。 是吗?

此外,parallelism-factor的含义以及fork-join-executorthread-pool的区别如何?

另外 - 文档应该说Async应该用于长时间的计算。 什么是长计算? 100ms的? 300ms的? 5秒? 10秒?我的估计会超过一秒,但如何确定?

这种质疑的原因是测试异步控制器调用比常规调用更难。您必须启动虚假应用程序并执行完整的请求,而不是仅调用方法并检查其返回值。

即使情况并非如此,我怀疑在AsyncAkka.future中包装所有内容是否正确。

我已经在#playframework IRC频道中提出了这个要求,但没有答案,似乎我不是唯一一个不确定应该怎么做的人。

重申:

  1. 每个请求是否从/ actions pool中分配一个actor是不对的?
  2. parallelism-factor是什么意思,为什么是1?
  3. fork-join-executorthread-pool-executor的区别如何?
  4. 计算需要多长时间才能包含在Async
  5. 如果不启动假应用程序,是不是可以测试异步控制器方法?
  6. 提前致谢。

    编辑:来自IRC的一些内容

    来自IRC的一些东西。

    <imeredith> arturaz: i cant be boethered writing up a full reply but here are key points
    <imeredith> arturaz: i believe that some type of CPS goes on with async stuff which frees up request threads
    <arturaz> CPS?
    <imeredith> continuations
    <imeredith> when the future is finished, or timedout, it then resumes the request
    <imeredith> and returns data
    <imeredith> arturaz: as for testing, you can do .await on the future and it will block until the data is ready
    <imeredith> (i believe)
    <imeredith> arturaz: as for "long" and parallelism - the longer you hold a request thread, the more parrellism you need
    <imeredith> arturaz: ie servlets typically need a lot of threads because you have to hold the request thread open for a longer time then if you are using play async
    <imeredith> "Is it right that every request allocates one actor from /actions pool?" - yes i belive so
    <imeredith> "What does parallelism-factor mean and why is it 1?" - im guessing this is how many actors there are in the pool?
    <imeredith> or not
    <imeredith> "How does fork-join-executor differ from thread-pool-executor?" -no idea
    <imeredith> "How long should a calculation be to become wrapped in Async?" - i think that is the same as asking "how long is a piece of string"
    <imeredith> "Is is not possible to test async controller method without spinning up fake applications?" i think you should be able to get the result
    <viktorklang> imeredith: A good idea is to read the documentation: http://doc.akka.io/docs/akka/2.0.3/general/configuration.html ( which says parallelism-factor is: # Parallelism (threads) ... ceil(available processors * factor))
    <arturaz> viktorklang, don't get me wrong, but that's the problem - this is not documentation, it's a reminder to yourself.
    <arturaz> I have absolutely no idea what that should mean
    <viktorklang> arturaz: It's the number of processors available multiplied with the factor you give, and then rounded up using "ceil". I don't know how it could be more clear.
    <arturaz> viktorklang, how about: This factor is used in calculation `ceil(number of processors * factor)` which describes how big is a thread pool given for your actors.
    <viktorklang> arturaz: But that is not strictly true since the size is also guarded by your min and max values
    <arturaz> then why is it there? :)
    <viktorklang> arturaz: Parallelism (threads) ... ceil(available processors * factor) could be expanded by adding a big of conversational fluff: Parallelism ( in other words: number of threads), it is calculated using the given factor as: ceil(available processors * factor)
    <viktorklang> arturaz: Because your program might not work with a parallelism less than X and you don't want to use more threads than X (i.e if you have a 48 core box and you have 4.0 as factor that'll be a crapload of threads)
    <viktorklang> arturaz: I.e. scheduling overhead gives diminishing returns, especially if ctz switching is across physical slots.
    <viktorklang> arturaz: Changing thread pool sizes will always require you to have at least basic understanding on Threads and thread scheduling
    <viktorklang> arturaz: makes sense?
    <arturaz> yes
    <arturaz> and thank you
    <arturaz> I'll add this to my question, but this kind of knowledge would be awesome docs ;)
    

2 个答案:

答案 0 :(得分:6)

  1. 当消息到达演员和演员时,只要处理该消息,它就会保留该演员。如果您同步处理请求(在处理该消息期间计算整个响应),则此actor在响应完成之前无法处理其他请求。相反,如果您收到此请求后,可以将工作发送给另一个参与者,收到请求的参与者可以在第一个请求由其他参与者处理时开始处理下一个请求。

  2. 演员使用的线程数是“num cpus * parallelism-factor”(但你可以指定min和max)

  3. 说不上

  4. 除非进行真正的计算,否则我倾向于将任何与其他系统交谈的异步做出异步,比如使用数据库/文件系统进行io。当然任何可能阻止线程的东西。但是,由于传递消息的开销很小,我不认为将所有工作发送给其他参与者会有问题。

  5. 请参阅Play Documentation on functional tests了解如何测试控制器。

答案 1 :(得分:1)

您似乎可以执行此操作进行测试:

object ControllerHelpers {
  class ResultExtensions(result: Result) {
    /**
     * Retrieve Promise[Result] from AsyncResult
     * @return
     */
    def asyncResult = result match {
      case async: AsyncResult => async.result
      case _ => throw new IllegalArgumentException(
        "%s of type %s is not AsyncResult!".format(result, result.getClass)
      )
    }

    /**
     * Block until result is available.
     *
     * @return
     */
    def await = asyncResult.await

    /**
     * Block until result is available.
     *
     * @param timeout
     * @return
     */
    def await(timeout: Long) = asyncResult.await(timeout)

    /**
     * Block for max 5 seconds to retrieve result.
     * @return
     */
    def get = await.get
  }
}

  implicit def extendResult(result: Result) =
    new ControllerHelpers.ResultExtensions(result)


  val result = c.postcodeTimesCsv()(request(params)).get
  status(result) should be === OK