2011-06-30 22 views
27

¿Cómo funciona el modelo de actor (en Akka) cuando necesita realizar E/S (es decir, una operación de base de datos)?¿Cómo funciona la E/S en Akka?

Tengo entendido que una operación de bloqueo lanzará una excepción (y esencialmente arruinará toda la concurrencia debido a la naturaleza de Netty, que Akka usa). Por lo tanto, tendría que usar un Future o algo similar; sin embargo, no entiendo el modelo de simultaneidad.

  1. ¿Puede 1 actor procesar múltiples mensajes simultáneamente?
  2. Si un actor realiza una llamada de bloqueo en un future (es decir, future.get()) eso bloquea solo la ejecución del actor actual; o ¿evitará la ejecución en todos los actores hasta que se complete la llamada de bloqueo?
  3. Si bloquea toda la ejecución, ¿cómo el uso de una concurrencia de asistencia futura (es decir, no invocar las llamadas de bloqueo en un futuro aún equivale a crear un actor y ejecutar la llamada de bloqueo)?
  4. ¿Cuál es la mejor manera de tratar con un proceso de varias etapas (es decir, leer de la base de datos, llamar a un servicio web de bloqueo, leer de la base de datos, escribir en la base de datos) donde cada paso depende de la última?

El marco básico es el siguiente:

  • estoy usando un servidor WebSocket que mantendrá miles de sesiones.
  • Cada sesión tiene algún estado (es decir, detalles de autenticación, etc.);
  • El cliente de Javascript enviará un mensaje JSON-RPC al servidor, que lo pasará al actor de sesión apropiado, que lo ejecutará y devolverá un resultado.
  • La ejecución de la llamada RPC implicará algunas llamadas de E/S y bloqueo.
  • Habrá un gran número de solicitudes concurrentes (cada usuario realizará una gran cantidad de solicitudes a través de la conexión WebSocket y habrá una gran cantidad de usuarios).

¿Hay una manera mejor de lograr esto?

+0

Me sorprende que nadie haya mencionado la IO asincrónica con un enfoque similar al Node.js/Twisted/gevent, etc. –

Respuesta

27

Las operaciones de bloqueo no arrojan excepciones en Akka. Usted puede bloquear llamadas de un actor (que probablemente desee minimizar, pero esa es otra historia).

  1. no, 1 actor instance no puede.
  2. No bloqueará a ningún otro actor. Puede influir en esto utilizando un despachador específico. Los futuros usan el despachador predeterminado (el evento impulsado por eventos globales normalmente) para que se ejecute en un subproceso en un grupo. Puede elegir qué despachador desea utilizar para sus actores (por actor o para todos). Supongo que si realmente quisieras crear un problema, podrías pasar exactamente el mismo despachador (basado en el hilo) a futuros y actores, pero eso requeriría algo de tu parte. Supongo que si tienes una gran cantidad de futuros bloqueando indefinidamente y el ejecutorervicio se ha configurado con una cantidad fija de hilos, puedes hacer explotar el servicio del ejecutor. Así que un montón de 'si'. a f.get bloquea solo si el futuro aún no se ha completado. Bloqueará el 'hilo actual' del actor desde el que lo llamas (si lo llamas desde un Actor, que no es necesario por cierto)
  3. no tienes que bloquear necesariamente. puede usar una devolución de llamada en lugar de f.get. Incluso puedes componer futuros sin bloquear.echa un vistazo a la charla de Viktor sobre "el futuro prometedor de Akka" para más detalles: http://skillsmatter.com/podcast/scala/talk-by-viktor-klang
  4. Usaría comunicación asíncrona entre los pasos (si los pasos son procesos significativos por sí mismos), así que use un actor para cada paso, donde cada actor envía un mensaje de ida al siguiente, posiblemente también mensajes de un solo sentido a algún otro actor que no bloqueará y que podrá supervisar el proceso. De esta forma podrías crear cadenas de actores, de las cuales podrías hacer muchas, frente a ellas podrías poner un actor de equilibrio de carga, de modo que si un actor bloquea en una cadena, otro del mismo tipo podría no estar en la otra cadena. Eso también funcionaría para su pregunta de 'contexto', pase de la carga de trabajo a los actores locales, colóquelos detrás de un agente de equilibrio de carga.

En cuanto a netty (y supongo que te refieres a Remote Actors, porque esto es lo único que se usa en netty Akka), pasa de tu trabajo lo antes posible a un actor local o un futuro (con devolución de llamada) si está preocupado por el tiempo o la prevención de que Netty haga su trabajo de alguna manera.

10

Las operaciones de bloqueo generalmente no arrojarán excepciones, pero esperar en un futuro (por ejemplo, mediante el uso de los métodos de envío !! o !!!) puede generar una excepción de tiempo de espera. Es por eso que debes seguir disparando y olvidando tanto como sea posible, usar un valor de tiempo de espera significativo y preferir las devoluciones de llamada cuando sea posible.

  1. Un actor no puede akka explícitamente proceso varios mensajes en una fila, pero se puede jugar con el valor throughput a través del archivo de configuración. El actor procesará varios mensaje (es decir, su método de recepción se llama varias veces secuencialmente) si la cola de mensajes no está vacío: http://akka.io/docs/akka/1.1.3/scala/dispatchers.html#id5

  2. operaciones bloquear dentro de un actor no va a "bloquear" a todos los actores, pero si se comparte hilos entre actores (uso recomendado), uno de los hilos del despachador se bloqueará hasta que se reanuden las operaciones. Así que intente componer futuros tanto como sea posible y tenga cuidado con el valor del tiempo de espera).

3 y 4. Acepto las respuestas de Raymond.

1

Lo que dijeron Raymond y paradigmático, pero también, si desea evitar pasar hambre en el grupo de subprocesos, debe cerrar cualquier operación de bloqueo en scala.concurrent.blocking.

Por supuesto, es mejor evitar las operaciones de bloqueo, pero a veces es necesario utilizar una biblioteca que bloquea. Si ajusta dicho código en blocking, permitirá que el contexto de ejecución sepa que puede estar bloqueando este hilo para que pueda asignar otro si es necesario.

El problema es peor de lo que describe paradigmático ya que si tiene varias operaciones de bloqueo puede terminar bloqueando todos los hilos en el grupo de subprocesos y no tener subprocesos libres. Podría terminar con un punto muerto si todos sus hilos están bloqueados en algo que no sucederá hasta que se programe otro actor/futuro.

He aquí un ejemplo:

 
import scala.concurrent.blocking 
... 

Future { 
    val image = blocking { load_image_from_potentially_slow_media() } 
    val enhanced = image.enhance() 
    blocking { 
    if (oracle.queryBetter(image, enhanced)) { 
     write_new_image(enhanced) 
    } 
    } 
    enhanced 
} 

documentación es here.

Cuestiones relacionadas