Play框架2.0的控制器/异步工作原理是什么?

15
我最近转向使用Play框架2.0,有一些问题困扰着我,关于控制器在Play中的实际工作方式。
Play文档中提到:

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

然而,在文档的另一部分中也提到:
            /actions {
                router = round-robin
                nr-of-instances = 24
            }

并且

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

看起来控制器处理程序有24个演员被分配。我猜每个请求都会为其生命周期分配其中一个演员。这是正确的吗?
另外,parallelism-factor是什么意思?fork-join-executorthread-pool有何不同?
还有 - 文档应该说异步应该用于长时间计算。什么样的计算可以称为长时间计算?100ms?300ms?5秒?10秒?
这些问题的原因是测试异步控制器调用比普通调用要困难得多。您必须启动一个虚拟应用程序并进行完整的请求,而不仅仅是调用方法并检查其返回值。
即使那不是这种情况,我也怀疑在AsyncAkka.future中包装所有内容是否正确的方式。
我已经在#playframework IRC频道中提出了这个问题,但没有得到答案,似乎我不是唯一一个不确定该如何做的人。
再次强调:
1. 每个请求是否从/actions池中分配一个演员? 2. parallelism-factor是什么意思,为什么是1? 3. fork-join-executorthread-pool-executor有何不同? 4. 计算需要多长时间才能被包装在Async中? 5. 没有启动虚拟应用程序,是否无法测试异步控制器方法?
提前感谢。
<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个回答

6
  1. 当一个消息到达一个 actor 时,它会在处理该消息的过程中一直持有该 actor。如果您同步处理请求(在处理该消息期间计算整个响应),则此 actor 直到响应完成前无法服务其他请求。如果您可以在接收到此请求时将工作发送给另一个 actor,那么接收到该请求的 actor 可以在其他 actor 处理第一个请求时开始处理下一个请求。

  2. 用于 actors 的线程数为“num cpus * parallelism-factor”(但您可以指定最小值和最大值)。

  3. 不知道

  4. 除非存在真正的计算,否则我倾向于将与其他系统通信的任何内容异步化,例如使用数据库/文件系统进行 io。当然,任何可能阻塞线程的东西都要这样做。然而,由于通过传递消息几乎没有开销,因此我认为只需将所有工作发送到其他 actor 就不会有问题。

  5. 有关如何测试您的控制器,请参见Play Documentation on functional tests


但是将任务发送给另一个执行者也会导致上下文切换。这有什么好处? - denis
通过不占用正在回答Web请求的线程,可以服务于下一个挂起的Web请求。 - stew
如果您将任务发送到另一个actor,那么处理新任务的actor是否会从服务http请求的actor池中删除,还是会有第二个actor池,或者会生成一个新的actor?在play2中使用异步请求时,我似乎没有完全掌握大局。 - fabspro
嗯,在做了更多的阅读之后,似乎情况是这样的:有一个固定的演员池,异步请求不会释放演员,只是允许您在单个请求中并行处理请求,以便整个请求可能更快地完成?因此,实际上,playframework应用程序需要增加其演员数量,直到应用程序达到CPU或IO限制为止,此时更多的演员将无法带来好处。 - fabspro
一般来说,我建议阅读 akka 的文档以获取更多信息。非常有帮助。 - fabspro
显示剩余2条评论

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接