scala akka actor typedactor

scala - Akka TypedActor vs. escribir mi propia interfaz estática en una clase de actor



(3)

He estado usando Akka y Scala durante aproximadamente un mes y me molesta un poco reemplazar las interfaces explícitas con mensajes. Considera el siguiente simple actor Akka:

case class DoMyHomework() class Parent extends Actor { def receive = { case d: DoMyHomework => // do nothing } }

Código de actor o no actor que envía a este actor un mensaje DoMyHomework como este:

ActorRef parent = ... parent.ask(DoMyHomework)

No tiene idea de cuál será el resultado. ¿Cuál es el tipo de respuesta? ¿Alguna vez obtendré una respuesta? ¿Puedo obtener una excepción? Y así.

La solución parece ser documentar la clase de casos ... pero, ¿y si algún otro actor también recibe la misma clase de casos? Entonces la documentación para recibir ese mensaje debe estar en el propio actor.

En un esfuerzo por limpiar esto un poco, pensé en hacer lo siguiente:

trait SomeoneSmarter { def wouldYouDoMyHomework: Future[Boolean] } class Parent extends Actor with SomeoneSmarter { case class DoMyHomework() def wouldYouDoMyHomework = { (self ? DoMyHomework()).mapTo(Boolean) } def receive = { case d: DoMyHomework => // TODO: If I''m busy schedule a false "No way" reply for a few seconds from now. // Just to keep their hopes up for a while. Otherwise, say sure right away. } }

Entonces, conversé con colegas sobre esto y una de las reacciones fue "no estás siendo fiel al modelo de actor".

En primer lugar, realmente agradecería la guía de personas que han estado usando Actors durante más tiempo. ¿Todos los mensajes se vuelven difíciles de manejar? ¿Terminas escondiendo el paso de mensajes detrás de las interfaces?

Los actores que propongo aún tienen la opción de enviar mensajes entre ellos, suscribirse a secuencias de eventos, todo lo que esperas de Akka. Y la interfaz te da una forma comprobada de saber con qué estás hablando. Y ayuda cuando se codifica en IDEs, y así sucesivamente. ¿Y por qué el usuario de un actor necesita saber que es un actor (a menos que también sea un actor y esté muy unido a él)?

La otra reacción que obtuve fue "parece que quieres un TypedActor". Pero después de leer sobre TypedActor no estoy convencido. Ciertamente, TypedActor me ahorra la molestia de crear esos mensajes internos. Pero, al menos a partir del ejemplo de código en http://doc.akka.io/docs/akka/snapshot/scala/typed-actors.html tengo la impresión de que el TypedActor solo funciona como un proxy alrededor de un bloque. del código que desea encapsular, o hacer seguro para subprocesos, o simplemente no llamar directamente desde su subproceso actual. Y lo que usted codifica es solo la implementación y la interfaz. No te metas con el actor en sí (el proxy), por ejemplo, si quieres que tu implementación realice un trabajo periódico o se suscriba a un flujo de eventos, o haga cualquier otra cosa que no esté relacionada con la interfaz.

También he leído http://letitcrash.com/post/19074284309/when-to-use-typedactors y no encontré ese ejemplo más iluminador. Probablemente no estoy asimilando a TypedActor (no es que pretenda haber comprendido realmente a los Actores todavía).

Gracias de antemano por la ayuda.

Pino


Encapsulacion de actor

Primero déjame responder a un punto que creo que es muy importante. Tu dices:

¿Y por qué el usuario de un actor necesita saber que es un actor (a menos que también sea un actor y esté muy unido a él)?

Los actores son un paradigma de programación radicalmente diferente del OO convencional, la principal diferencia es que todo es asíncrono y, por lo tanto, nunca hay "valores de retorno" reales. Esto significa que, por lo general, es una mala idea ocultar el hecho de que es un actor; para excepciones, consulte http://letitcrash.com/post/19074284309/when-to-use-typedactors . Lo mejor de los actores es que están totalmente encapsulados, en Akka detrás de ActorRef , en oposición a la encapsulación débil de los idiomas OO. Para obtener el máximo ActorRef , exponga ActorRef s siempre que sea posible, lo que le brinda al código del cliente la oportunidad de usarlos de la manera más apropiada (que puede estar usando tell o ask según el contexto).

Estructurando tus mensajes

Al escribir un actor, debe colocar todo sobre este actor en un lugar, incluida la descripción de la interfaz del contrato. Podría verse un poco así:

object Parent { /** * Send this message to make your parent do your homework … yeah, right ;-) */ case object DoHomework } /** * This actor will do your homework if asked to. * * ==Actor Contract== * * ===Inbound Messages=== * - ''''''DoHomework'''''' will ask to do the homework * * ===Outbound Messages=== * - ''''''HomeworkResult'''''' is sent as reply to the ''''''DoHomework'''''' request * * ===Failure Modes=== * - ''''''BusinessTripException'''''' if the parent was not home * - ''''''GrumpyException'''''' if the parent thinks you should do your own homework */ class Parent extends Actor { … }

Las diferencias con TypedActor

El uso de actores sin estilo normales le permite utilizar todo el poder del modelo de actor, incluido el cambio dinámico del comportamiento, y no tener la tentación de encerrarse en la jaula de llamadas "sincrónicas" con vigilancia de tiempo de espera (en resumen, los TypedActors son más útiles cuando implementando una interfaz sincrónica tradicional usando actores detrás de escena). Estoy de acuerdo en que el soporte de IDE para los tipos de mensajes sería bueno, pero ese es un problema de la herramienta (he estado hablando con el equipo de ScalaIDE acerca de agregar algo de magia, pero eso tendrá que esperar hasta que pueda recibir prioridad). Tener todas las propiedades del actor definidas en un solo lugar es la parte importante.


Descargo de responsabilidad: no soy un experto Akka / Actores. He estado trabajando con Actors y Akka durante 18 meses y todavía estoy tratando de comprender ciertos conceptos, especialmente cuando no uso Akka.

Para el caso particular y limitado en el que desea saber el tipo de retorno de un futuro Akka, sí, debe usar un TypedActor. Las pocas veces que he usado TypedActors se usaron para proporcionar una API a un módulo que se encontraba fuera del sistema Actor. Es decir, he construido un sistema sobre Akka que realizó la mayor parte de su trabajo dentro de una red Akka, pero tenía uno o dos módulos fuera de la red Akka que requerían acceso a las funciones proporcionadas por la red Akka. El más notable fue una interfaz de Scalatra que llamó a la red Akka e hizo algunos trabajos sobre los valores devueltos por la red Akka antes de responder a su cliente. El TypedActor, sin embargo, era realmente una interfaz para la red Akka. Considero que usar un TypedActor como una interfaz de la API para módulos externos (externos a la red Akka) es otra separación de inquietudes.

En general, estoy de acuerdo con los que te dicen que "no estás siendo fiel al modelo de actor" al tratar de forzar una vista de los tipos de retorno. En su forma más pura, y en la forma en que he tenido más éxito, el modelo Actor se implementa utilizando la semántica del fuego y el olvido. Los mensajes no se vuelven difíciles de manejar y en muchos casos han ayudado a organizar mi código y a definir los límites de trabajo. Ayuda a ponerlos en su propio paquete.

Si implementara la característica que ha descrito, se vería como la siguiente:

trait SomeoneSmarter { def wouldYouDoMyHomework : Boolean } class Response() case class NoWay() extends Response case class Sure() extends Response class ActorNetworkFrontEnd extends Actor { def receive = { case d: DoMyHomework => busy match { case true => sender ! NoWay() case false => sender ! Sure() } } } case class SomeoneSmarter(actorNetworkFrontEnd:ActorRef) extends SomeoneSmarter { def wouldYouDoMyHomework : Boolean = { val future = actorNetworkFrontEnd ? DoMyHomework() val response = Await.result(future, timeout.duration).asInstanceOf[Response] response match { case NoWay() => false case Sure() => true } } }

Tenga en cuenta que la forma en que escribí le gustaría hacer mi trabajo en casa, se bloqueará mientras espera una respuesta. Sin embargo, hay maneras inteligentes de hacer esto de forma asíncrona. Consulte http://doc.akka.io/docs/akka/2.0.3/scala/futures.html para obtener más información.

Además, tenga en cuenta que una vez que su mensaje esté dentro de la red Akka, puede hacer todo el proceso de escalado y comunicación remota y el usuario de su API de TypedActor nunca tendrá que saberlo.

Hacer esto agrega cierta complejidad en proyectos grandes, pero si considera que separa la responsabilidad de proporcionar una API a módulos externos y tal vez incluso transfiera esa responsabilidad a otro paquete, es muy fácil de administrar.

Buena pregunta. No puedo esperar a escuchar las respuestas de los desarrolladores Akka más experimentados.


El modelo de cálculo Actor tiene grandes similitudes con la programación orientada a objetos. OO es sobre la transferencia indirecta de control. Cuando llama a un método (enviar un mensaje), pierde el control del mensaje. Por supuesto, los lenguajes de programación estáticos lo ayudan un poco con todo lo bueno de la verificación de tipos, pero aparte de eso, no tienen idea de lo que sucederá con el mensaje. Demonios, tal vez el método nunca regrese, aunque el tipo de retorno claramente dice que lo hará (excepción lanzada, bloqueo en vivo, lo que sea ...)! De acuerdo, cuando solías usar Java o, mejor aún, Scala, apesta el hecho de abandonar la escritura estática, pero no es que no obtengas ningún beneficio. La escritura dinámica le da acoplamiento suelto. Por ejemplo, no es necesario crear interfaces adicionales solo para presentar un actor simulado para sus pruebas; ActorRef es la única API que necesita.