2012-09-25 20 views
16

Recientemente me cambié a Play Framework 2.0 y tengo algunas preguntas sobre cómo los controladores realmente funcionan en el juego.¿Cómo funciona exactamente Play Framework 2.0 controllers/Async?

En play docs se mencionan:

Debido a la forma de juego 2.0 funciona, el código de acción debe ser tan rápido como sea posible (es decir, no de bloqueo.).

Sin embargo, en another part of the docs:

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

y

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

Parece que hay 24 agentes asignados a los controladores de manipulación. Supongo que cada solicitud asigna uno de esos actores para la duración de la solicitud. ¿Es correcto?

Además, ¿qué significa y cómo se parallelism-factorfork-join-executor difieren de thread-pool?

También - los documentos deberían decir que Async debería usarse para cálculos largos. ¿Qué califica como un cálculo largo? 100 ms? 300 ms? ¿5 segundos? ¿10 segundos? Mi conjetura sería algo más de un segundo, pero ¿cómo determinar eso?

La razón de este cuestionamiento es que la prueba de las llamadas al controlador asíncrono es mucho más difícil que las llamadas regulares. Tienes que girar una aplicación falsa y hacer una solicitud en toda regla en lugar de simplemente llamar a un método y verificar su valor de retorno.

Incluso si ese no fuera el caso, dudo que envolver todo en Async y Akka.future es el camino.

Lo he pedido en el canal de IRC#playframework pero no hubo respuesta y parece que no soy el único que no está seguro de cómo se deben hacer las cosas.

Sólo para reiterar:

  1. Es correcto que cada petición asigna un actor/piscina de acciones?
  2. ¿Qué significa parallelism-factor y por qué es 1?
  3. ¿Cómo se diferencia fork-join-executor de thread-pool-executor?
  4. ¿Cuánto tiempo debe finalizar un cálculo en Async?
  5. ¿No es posible probar el método de controlador asíncrono sin girar las aplicaciones falsas?

Gracias de antemano.

Editar: algunas cosas de IRC

algunas cosas de 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 ;) 

Respuesta

6
  1. Cuando llega un mensaje en un actor actor, que se aferra a que el actor, siempre que sea necesario para procesar el mensaje. Si procesa la solicitud de forma síncrona (calcule la respuesta completa durante el procesamiento de ese mensaje), este actor no podrá atender otras solicitudes hasta que se complete la respuesta. Si en cambio puede, al recibir esta solicitud, enviar el trabajo a otro actor, el actor que recibió la solicitud puede comenzar a trabajar en la siguiente solicitud mientras la otra solicitud está siendo trabajada por otros actores.

  2. El número de hilos utilizados para los actores es "CPU num * paralelismo factor" (sin embargo, puede especificar valores mínimo y máximo)

  3. No sé

  4. A menos que haya cálculos reales pasando, Tienden a sincronizar cualquier cosa que esté hablando con algún otro sistema, como hacer io con una base de datos/sistema de archivos. Ciertamente cualquier cosa que pueda bloquear el hilo. Sin embargo, dado que hay muy poca carga en los mensajes que pasan, no creo que haya un problema con solo enviar todo el trabajo a otros actores.

  5. Consulte el Play Documentation on functional tests para saber cómo probar los controladores.

+0

Sobre el punto 1. ¿Cuál es la ventaja de enviar tareas fuera a otros actores en lugar de simplemente aumentar el número de hilos de/acciones que LIKE 150 (para 150 acciones concurrentes)? – arturaz

+0

Piénsalo de esta manera. Tienes cientos de cosas que hacer en tu escritorio. ¿Cuál sería más eficiente? sacar uno de la pila, trabajar en él hasta que esté hecho, luego trabajar en el siguiente. O tome los primeros 150 de ellos y trabaje un poquito en cada uno dividiendo su tiempo entre 150 cosas diferentes. El primero es más eficiente porque no pierde el tiempo en "interruptores de contexto". Lo mismo es cierto aquí. – stew

+0

Pero enviar la tarea a otro actor también lleva a un cambio de contexto. ¿Cuál es el beneficio? –

1

Parece que usted puede hacer esto para las pruebas:

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 
Cuestiones relacionadas