Archivo de la etiqueta: akka

Diapositivas de la charla de Commit Conf

Los días 23 y 24 de noviembre ha tenido lugar en Madrid la primera edición del Commit Conf, uno de los eventos más importantes que se tiene en lengua castellana.

En su momento presente mi charla sobre Akka Sharding y su relación con DDD al “call for papers” con ilusión pero sin grandes esperanzas de que fuera aceptada. Hay que tener en cuenta que hasta ese momento no tenía experiencia presentando charlas en eventos públicos aunque afortunadamente si que tengo cierto callo en exponer ideas en público ya que mi trabajo me lo exige.

Así que cuando me aceptaron la presentación me lleve una enorme alegría porque consideraron el tema de mi charla interesante. Alegría, pero también responsabilidad por toda la confianza depositada.

Recuerdo con una sonrisa la primera presentación que hice a  a tres compañeros de trabajo que me hicieron de beta testers. Para mi alivio les resultó interesante y fue curioso, porque cada uno tenía una visión diferente de lo que le había gustado de la presentación. Uno se decantó más por las posibilidades que ofrecía este paradigma, otro puso más interés como se implementaba y el otro las implicaciones operacionales que tenía el despliegue de un sistema distribuido. El problema fue el tiempo: se fue más de hora y media.

Luego he tenido la increíble oportunidad de presentar la charla en otros foros como puede ser en el ScalaMAD (el 31 de octubre) a los que estoy agradecido y que me permitió pulir la charla.

Al final creo que la presentación en el Commit fue bastante bien, aunque recortada estuvo un poco justa de tiempo, … me faltaron unos minutos, un problema endémico por intentar tener en cuenta todos los conceptos, pero si que la gente se mostró participativa en la tanda de preguntas lo cual es muy bueno.

En resumen la sensación fue muy positiva, una experiencia intensa pero muy bonita de la que guardaré un buen recuerdo. Para terminar, lo prometido, estos son los enlaces de la charla y el repositorio del github de los notebook de Jupiter con la demo.

Presentación: Akka Sharind y su relación con DDD

[Logo GitHub]Código: Repo GitHub

Actores tipados, Akka por ejemplos

“Tercer y último artículo sobre Akka explicado mediante ejemplos.

En los dos anteriores se basó en como funcionan la cola de mensajes de un actor, que es un router, como se envían mensajes entre actores. Después se trató temas como la supervisión y el bus de eventos.

Ahora me centraré en los actores tipados.

Entrada anterior: Akka por ejemplos. Sobre supervisión, bus de eventos, …

Todo el código fuente relacionado con esta serie de posts está disponible en mi repositorio de GitHub

[Logo GitHub] AkkaActorsAndFutures


Actores tipados

Los actores tipados son una funcionalidad que ofrece Akka recubriendo un actor no tipado en un interfaz java. Con un actor tipado el programador realiza llamadas a un método de una clase normal ‘sin preocuparse‘ (y pongo esto entre comillas por lo que veremos más adelante) de lo que está detrás es un actor de Akka.

[Actor tipado]

[Actor tipado]

Permite unir los dos mundos, código java estándar con el mundo de los actores. En la documentación de Akka puedes leer que tienen su nicho, pero hay que ‘usarlos con moderación‘. Recomiendan que si lo que buscas es una arquitectura de alta escalabilidad utilizar estos actores no es la solución más sencilla. Por ejemplo en este caso, aunque se puede, no es tan natural la supervisión y la monitorización padre-hijo.

Para los actores tipados, Akka utiliza el patrón de Objetos Activos y proxys de Java. Para levantar un actor tipado es necesario una interfaz y una implementación. Simplificando, en los actores tipados no existe el método ‘onReceive()‘ y se utilizan como un clase más de Java haciendo llamadas a los métodos que exporta la interfaz.

En realidad lo que hay detrás es un actor no tipado. Akka monta un proxy en el que el interfaz será la fachada y el ‘contrato público‘ que podrá utilizar el resto del código java. Los atributos de los métodos de ese interfaz serán los mensajes que se enviarán al actor y la implementación será la que dicte el comportamiento. Por lo demás se cumple con el modelo de actores que asegurá que no habrá más de un hilo ejecutando el actor.

Todo esto, que haya un actor detrás de este interfaz, hace que tenga un comportamiento diferente al esperado en una clase java, y hay que tenerlo en mente cuando se utilizan actores tipados.

Para los ejemplos se ha utilizado una clase TypedActorDummyImp que implementa el interfaz TypedActorDummy con una serie de métodos sencillos que permitirán ver con facilidad el funcionamiento de los actores tipados.

He creado dos test, uno TypedActorDummyImpTest.java que comprueba el comportamiento en una llamada normal a un método con diferentes tipos de retorno y otro TypedActorDummyImpExceptionsTest.java para ver cual será su comportamiento si se produce una excepción.

Comportamiento según el tipo de retorno.

Un método puede devolver tres tipos de respuesta: o bien es un método que no devuelve nada (void), o devuelve un objeto, o bien devuelve un futuro de objeto.

Cuando un método devuelve no devuelve nada: void

Cuando un actor tipado el tipo de respuesta es ‘void‘, lo que está ocurriendo es que se está mandando un mensaje del tipo dispara y olvida (‘fire-forget‘). En realidad se está enviado un mensaje ‘tell‘ al actor que hay por detrás, y no se espera respuesta.

Esto hace que tenga un comportamiento un poco ‘diferente‘ del esperado y es algo que se puede comprobar en los dos ejemplos.

Test 1: Llamada normal

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.typed.TypedActorDummyImpTest#testReturnVoidWithSleep test

En este test se levanta un actor tipado que tiene un método que devuelve void. Se simula una operación costosa con un “Thread.sleep”.

Se comprueba que no se espera a la ejecución del método, sino que se continua inmediatamente con la siguiente orden. El mensaje se ha encolado al actor y este ejecutará la implementación en segundo plano, sin ningún tipo de espera en el hilo principal.

Test 2: Cuando se produce una excepción

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.typed.TypedActorDummyImpExceptionsTest#testRuntimeExceptionVoid test

Como consecuencia de que el mensaje es enviado al actor, ejecutándose en un segundo plano, el hilo principal no es capaz de saber si ha ocurrido un error en la ejecución. Si se produce una excepción no será recogida desde la llamada al método.

El test anterior permite hacer esta comprobación y es bastante didáctico.

Cuando un método devuelve un futuro

Test 1: Llamada normal

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.typed.TypedActorDummyImpTest#testFutureEcho test

Cuando se llama a un método de un actor tipado que devuelve un futuro, el comportamiento sería el mismo que si se enviara un mensaje de petición y respuesta no bloqueante. Es el equivalente al envío de un mensaje con ‘Patterns.ask(…)’ de un actor no tipado.

Por lo tanto la respuesta se recogerá cuando se resuelve el futuro y no desde la llamada al método. El hilo principal continuaría su ejecución sin esperar.

Test 2: Cuando se produce una excepción

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.typed.TypedActorDummyImpExceptionsTest#testRuntimeExceptionFuture test

Lo mismo pasaría si se produjera una excepción, sería devuelta en la resolución del futuro y no sería recogida en la llamada al método tipado. Esto entra dentro de lo lógico si pensamos que cuando se produce el error es en el momento en el que se procesa el mensaje por parte del actor.

Cuando un método devuelve un objeto

Test 1: Llamada normal

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.typed.TypedActorDummyImpTest#testEcho test

Test 2: Cuando se produce una excepción

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.typed.TypedActorDummyImpExceptionsTest#testRuntimeExceptionString test

Cuando se llama a un método de un actor tipado que devuelve un objeto, el comportamiento es ‘parecido‘ al esperado. Y digo ‘parecido‘ porque en realidad lo que se está haciendo es enviando el mensaje al actor y esperando la respuesta (siguiendo un patrón de petición-respuesta bloqueante)

Por lo tanto desde la llamada al método de un actor tipado hace que hilo de ejecución principal espere la respuesta (aunque por detrás se esté enviando un mensaje ejecutándose en otro ‘hilo’). Por lo tanto el comportamiento es ‘el mismo’ al acostumbrado (ver el ejemplo 1). En este caso si se produce una excepción si será recogida en la llamada al método (Ver test 2)


Como conclusión final de esta serie de posts decir que aunque el modelo de actores y Akka presentan nuevos conceptos, estos una vez aprendidos son sencillos de asimilar.

A cambio ofrece un modo elegante y sencillo de codificar aplicaciones que soporten programación paralela  que sean escalables, robustas y  de alta disponibilidad.

Espero que os sirva.

M.E.

Akka por ejemplos. Sobre supervisión, bus de eventos, …

Esta es la segunda entrega de Akka por ejemplos. La primera trató sobre cual es el funcionamiento de la cola de mensajes de un actor, que es un router y como se envían mensajes entre actores.

Ahora se tratarán temas como el control de fallos y la supervisión de actores, el bus de eventos de Akka y sobre los ‘Dead Letters’

Entrada anterior: Akka por ejemplos

Sin más vamos con los ejemplos.

Todo el código fuente relacionado con esta serie de posts está disponible en mi repositorio de GitHub

[Logo GitHub] AkkaActorsAndFutures


Control de fallos: Supervisión y monitorización.

[Supervisión padre-hijo en Akka]

[Supervisión padre-hijo en Akka]

En Akka la robustez a fallos se basa en la supervisión de actores con jerarquía padre-hijo. El actor que delega tareas en los actores que crea es el que ‘supervisa‘ y decide que hacer cuando se produce un fallo en alguno de sus hijos.

Se crea así una jerarquía en forma de árbol en la que la que los actores padres supervisan a los actores hijos. El actor ‘user‘ de Akka será el padre de primera generación de actores que hayamos creado.

Esto es realmente lo que te da la robustez (‘resilence‘)  que un sistema reactivo necesita. Esta robustez se pierde cuando se intenta en vez de tener esta jerarquía, ir a un esquema con una mentalidad más tradicional de inyección de dependencias y se cae en la tentación de inyectar actores a otros actores.

Cuando un actor falla y se produce una excepción, su supervisor puede hacer, o bien reiniciar el actor, parar el actor, escalar la excepción o absorber la excepción y continuar con la ejecución.

El sistema de supervisión de Akka permite tener la posibilidad de diferente respuesta del supervisor dependiendo del tipo de excepción y si esta afecta a todos los actores supervisados o sólo al que ha fallado. Esto junto con lo anterior permite implementar fácilmente patrones,  por ejemplo de tipo ‘circuit breaker‘, si uno de los actores falla.

Akka tiene la posibilidad de establecer eventos o disparadores en el ciclo de vida de un actor. Por ejemplo antes de crearlo, antes y después de reiniciarlo y cuando se para.

Ademas del concepto de supervisión, en Akka existe el de monitorización. Cualquier actor, puede ‘monitorizar‘ a cualquier otro, lo haya creado o no. El primero obtiene un mensaje ‘Terminated‘ cuando el monitorizado ha terminado. Esto permite modificar su comportamiento o cambiar su estado. En los ejemplos siguientes el actor supervisor también hace de ‘monitor‘ de sus hijos.

Para poder mostrar con ejemplos como es el comportamiento de las diferentes estrategias a seguir, he creado en el proyecto dos actores: ActorNoTypedDummyCheckLifeCycle y ActorNoTypedLetItCrash.

ActorNoTypedDummyCheckLifeCycle se utiliza para comprobar los eventos que se disparan y por los ‘estados‘ que pasa un actor cuando es supervisado con diferentes estrategias. Concatena en un atributo ‘state‘ los diferentes pasos por los ‘hooks‘ del ciclo de vida de un actor:

  • Método ‘aroundPreStart‘: Primer evento que se dispara cuando se inicia un actor. Sólo se ejecuta cuando se inicia un actor por primera vez.
  • Método ‘preStart’: Se lanzan cuando se inicia un actor o después de que se haya reiniciado.
  • Método ‘aroundPreRestart’: Cuando un actor va a ser reiniciado, se dispara antes de parada el actor.
  • Método ‘preRestart‘: También se ejecuta antes de parar el actor cuando va a ser reiniciado. La llamada es después de la del método ‘aroundPreRestart
  • Método ‘postStop‘: Evento que se llama, o bien cuando se para el actor, o bien antes de reiniciarlo (en este último caso justo después de que se llame al método ‘preRestart‘)
  • Método ‘postRestart‘: Se lanza después de reiniciar el actor.
  • Método ‘aroundPostStop‘: Se realiza la llamada justo antes de parar definitivamente el actor.

El actor ActorNoTypedLetItCrash es el que creará y hará de supervisor de ActorNoTypedDummyCheckLifeCycle. En su constructor,  permite definir por parámetro la estrategia de supervisión. Su comportamiento será en de hacer de proxy en el envío de mensajes a  ActorNoTypedDummyCheckLifeCycle hasta que este el actor hijo se pare o cuando el mismo se haya reiniciado.

Para mostrar el comportamiento de las diferentes estrategias y cual es el ciclo de vida especifico de los actores (cuando se llama a los diferentes ‘hooks‘) he codificado en el proyecto los siguientes ejemplos.

Estrategia de supervisión por defecto

Test: ActorLetItCrashTestDefault.java

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.supervisorstrategy.ActorLetItCrashTestDefault test

Si se produce un excepción en un actor hijo, la estrategia por defecto en Akka es la de reiniciar el actor hijo, excepto si es un error irrecuperable:

  •  cuando se ha producido una excepción al inicializar el actor (ActorInitializationException)
  • cuando ya se había parado el actor(ActorKilledException)

Esta estrategia cobra sentido cuando se quiere mantener un sistema estable. La idea subyacente es que cuando se produce un error el sistema debería volver a un estado consistente y esto se consigue reiniciando el estado del actor.

En este ejemplo, los pasos son:

  1. Se envía un primer mensaje ‘state‘ al actor ActorNoTypedLetItCrash. Este hará el reenvío del mensaje al actor hijo (ActorNoTypedDummyCheckLifeCycle) que devolverá la concatenación de los diferentes métodos por los que ha pasado hasta ese momento.
  2. Después se envía un mensaje con una excepción para que el actor hijo provoque la excepción.
  3. Por último se vuelve a enviar un mensaje ‘state‘ para ver por los eventos que se ejecutan cuando se reinicia el actor.
  4. Finalmente para poder ver todo el flujo, se para el actor.

El ejemplo permite comprobar el flujo completo del paso de los diferentes eventos a lo largo de la vida del actor hijo. En este caso con la estrategia de supervisión por defecto, es este:

Se llama al constructor
 => Llamada a aroundPreStart
  => Llamada a preStart
   => [[exception]] Se provoca una excepción
    => Llamada a aroundPreRestart
     => Llamada a preRestart
      => Llamada a postStop
       => Llamada al constructor (Se vuelve a crear el actor)
        => Llamada a aroundPostRestart
         => Llamada a postRestart
          => Llamada a preStart
           => Llamada a aroundPostStop
            => Llamada a postStop

Estrategia de supervisión: Escalar la excepción

Test: ActorLetItCrashTestEscalate.java

Commando maven
mvn -Dtest=com.logicaalternativa.examples.akka.supervisorstrategy.ActorLetItCrashTestEscalate test


¿Qué ocurre cuando se elige una estrategia de supervisión de tipo ‘escalate‘? En este caso la excepción en el actor hijo provoca el error también en el actor supervisor. El comportamiento final depende entonces de la propia estrategia de supervisión de este último.

El ejemplo puede ayudar a verlo más claro. En este caso se configura el supervisor (ActorNoTypedLetItCrash) para que la estrategia de supervisión sea la de escalar la excepción. Cuando se provoca una excepción en el actor supervisado, el resultado es el mismo que si se hubiera producido en el supervisor.

El este caso el supervisor tiene la estrategia de supervisión por defecto, que lo reinicia, volviéndose a crear el actor hijo.

En el test se comprueba este hecho cuando se valida todo el ciclo de vida del actor hijo. Se arranca y después de la excepción, se para, para después volver a arrancar sin reinicio. Esto último provocado porque al reiniciarse actor supervisor, el hijo se para y se vuelve a crear:

Llamada a constructor
 => Llamada a aroundPreStart
  => Llamada a  preStart
   => [[exception]] Se provoca una excepción
    => Llamada a aroundPostStop
     => Llamada a postStop
      => Llamada al constructor (Se vuelve a crear el actor)
       => Llamada a aroundPreStart
        => Llamada a preStart
        => Llamada a aroundPostStop
         => Llamada a postStop

Estrategia de supervisión ‘resume’: ‘Absorber’ la excepción.

Test: ActorLetItCrashTestResume.java

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.supervisorstrategy.ActorLetItCrashTestResume test

Si el supervisor adopta una estrategia de ‘resume‘ si se produce un error la excepción no produce ni el reinicio, ni la parada del actor supervisado. Aunque se haya producido el error, sigue levantado a la espera de procesar nuevos mensajes.

El test ayuda a comprender este tipo de supervisión. Se comprueba que cuando se produce una excepción al procesar un mensaje, no se hace ninguna llamada a los métodos del ciclo de vida, ni tampoco se escala la excepción. El siguiente mensaje lo procesa correctamente.

El ciclo de vida del actor supervisado con este tipo de supervisión será:

Llamada a constructor
 => Llamada a aroundPreStart
  => Llamada a preStart
   => [[exception]] Se provoca una excepción
    => Llamada a aroundPostStop
     => Llamada a postStop

Estrategia de supervisión ‘stop’: Parar el actor supervisado.

Test: ActorLetItCrashTestStop.java

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.supervisorstrategy.ActorLetItCrashTestStop test

En este caso, se para el actor supervisado cuando al procesar un mensaje ocurre una excepción.

En el ejemplo se fuerza esta situación. Si se provoca una excepción en el actor supervisado se comprueba que el actor no se vuelve a reiniciar. De hecho si se le vuelve a enviar un mensaje de estado, este se envía a la cola de ‘Dead Letters‘ porque la referencia al actor ya no apunta a un actor válido.

El ciclo de vida del actor es el siguiente:

Llamada a constructor
 => Llamada a aroundPreStart
  => Llamada a  preStart
   => [[exception]] Se provoca una excepción
    => Llamada a aroundPostStop
     => Llamada a postStop

Los mismos pasos que con la estrategia ‘resume‘. Pero en este caso no ha sido necesario parar el actor en el test porque ‘muere‘ después de provocarse la excepción.

Bus de eventos interno de Akka

Test: PublishSimpleSuscribeTest.java

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.bus.PublishSimpleSubcribeTest test

Akka poseé un bus interno que permite el envío de mensajes siguiendo el patrón publicador-suscriptor. Un ‘publicador‘ envía un mensaje sin saber quien van a ser los receptores. Los actores que quieran recibir ese tipo de mensajes deberán suscribirse a esa publicación.

[Bus de eventos de Akka]El paradigma cambia. En vez de enviar un mensaje a un actor concreto, es decir, “(Por favor) haz esto”, cuando se publica un mensaje en el bus se está diciendo “(A quien pueda interesar) ha ocurrido esto

En este caso el ejemplo muestra un uso sencillo del bus de Akka para mostrar este patrón.

Se levantan dos actores suscriptores (del tipo ActorNoTypedLogEvent) que se suscribirán a mensajes de tipo Integer. La implementación de este actor es sencilla: registra los mensajes del tipo a los que está suscrito y si se le envía un mensaje ‘lastMessage‘ devolverá el último mensaje que ha registrado.

En el test después de levantar a los dos suscriptores se envía un mensaje al bus de Akka. Después se comprueba que el mensaje le ha llegado a ambos.

Dead letters

Test: DeadLettersTest.java

Comando maven
mvn -Dtest=com.logicaalternativa.examples.akka.bus.DeadLettersTest test

Un ‘dead letter‘ se produce cuando no se ha podido entregar un mensaje a un actor. Un caso típico es cuando el actor receptor del mensaje ya no existe. El ‘dead letter‘ contiene el mensaje enviado, la referencia al actor que lo envía y la del que lo iba a recibir.

Por defecto Akka informa en el log de la aplicación los dead letters que se producen (esto es configurable). También pública esta información a través del bus de eventos de Akka, lo que permite que una aplicación tenga la posibilidad de una gestión específica de estos mensajes perdidos.

En el ejemplo para poder provocar un dead letter se crea un actor del tipo ActorNoTypedDummy pararlo para a continuación enviarle un mensaje. Este mensaje ‘se pierde‘ y es recogido como ‘dead letter‘.

Por otra parte en el ejemplo se está utilizando un actor del tipo ActorNoTypedLogEvent configurado para que suscriba en el bus de eventos de Akka los mensajes del tipo DeadLetter.

Cuando se manda un mensaje de ‘lastMessage‘ al actor ActorNoTypedLogEvent este devolverá la información del mensaje perdido en forma de DeadLetter.

Como comprobación final del test se validará la información contenida en el objeto DeadLetter: el mensaje y la referencia al actor receptor.


Hasta aquí este segundo artículo de Akka por ejemplos. El próximo y último artículo de esta serie será sobre actores tipados.

Espero que os sirva.

M.E.

Akka por ejemplos

“Akka representa muchos conceptos nuevos que al principio cuesta asimilar. Para poder entenderlo mejor, he ido creando, poco a poco, una serie de ejemplos que me han permitido comprender mejor el paradigma que conlleva.

[Akkapor ejemplos]

Este artículo trata sobre esto, intentar tener una prueba de código de cada nuevo concepto que permita ver cual es su comportamiento y función.”

Entrada siguiente: Akka por ejemplos. Sobre supervisión, bus de eventos, …

Akka es un framework basado en el modelo de actores que permite dar solución al problema de la programación concurrente. Además de formar parte de Scala de manera nativa existe como librería en Java.

Permite diseñar aplicaciones que sean escalables horizontalmente de manera transparente, con alta toleracia a fallos y basada en eventos que permite cumplir el ‘manifiesto reactivo‘.

Este será el primer de una seríe de artículos explicando conceptos clave que se utilizan en Akka mediante ejemplos. En esta primera parte trataré como funciona las colas de mensajes de un actor, que es un router y las diferentes maneras de como se pueden enviar mensajes entre actores.

En próximas entregas trataré sobre el sistema de supervisión de los actores en Akka, como funciona el bus de eventos y que son los actores tipados.

Esta entrada es eminentemente práctica, te invito a descargarte el proyecto y a que eches un vistazo y ejecutes los ejemplos. Se entenderá mejor lo que quiero explicar en este artículo. Todo el código fuente relacionado con esta serie de post está disponible en mi repositorio de GitHub

[Logo GitHub] AkkaActorsAndFutures

Sobre los test

La idea fundamental a la hora de realizar los test ha sido la de realizar implementaciones dummy y ‘ad hoc‘ y, gracias a ellas, montar test que permitieran ver con facilidad cual es el funcionamiento de la pieza que interesa.

Por otra parte, he buscado que el código incluido en los test puedan servir de ejemplo uando se quiera implementar ese mismo concepto en una aplicación real.

Es por esto por lo que he evitado utilizar el framework de Akka de test ‘akka-testkit’ y en algunos momentos puede parecer que me he complicado la vida realizando las validaciones cuando se completan los futuros y utilizando agentes de Akka para compartir estados entre actores.

Mucha de la funcionalidad común a todos los actores, como crear el sistema de actores y métodos de utilidad se ha llevado a test base (TestBaseTestBaseTypedActor ) de los que heredan todos los test.

Prólogo: Los agentes

Ya que en los test utilizo agentes, antes de entrar a explicar los test, intentaré hacer una breve introdución de lo que es un agente en Akka.

Un agente de Akka permite almacenar un valor y actualizarlo sin problemas de concurrencia. Tienen la particularidad que la actualización se realiza de manera asíncrona, pero se puede leer el valor de manera síncrona.

Su funcionamiento es similar a un mailbox de un actor. Se basa en encolar los nuevos valores de tal manera que el valor interno se va actualizando en orden de llegada. No existe concurrencia y por eso su actualización es asíncrona.

Hay que tener en cuenta que cuando se pide su valor de manera síncrona devuelve el valor ‘actual‘ no el valor ‘futuro‘ que tendrá cuando se procesen todos los valores encolados hasta ese momento.

Vamos entonces con los test.


Como funciona la cola de mensajes de un Actor

Test: ActorQueueTest.java

Commando maven:
mvn  −Dtest=com.logicaalternativa.examples.akka.queue.ActorQueueTest test

[Un actor, un mailbox]

Un actor, un mailbox

El modelo de actores nos asegura que no puede haber más de una ejecución concurrente de un actor. Es decir, no puede haber más de un hilo ejecutando el código de actor. Los mensajes que se envían al actor se encolan en su ‘inbox‘ de tal manera que se ejecutan de uno en uno.

En este ejemplo se está levantando dentro del sistema de actores, un actor del tipo ActorNoTypedDummy. Este tiene una lógica muy sencilla: si se envía un mensaje tipo Long la ejecución se espera los milisegundos indicados en el valor del mensaje. Si el mensaje es de tipo String devuelve un eco del mensaje.

En el test ActorQueueTest se envía un primer mensaje tipo Long para que se realice una espera y después 10 mensajes de tipo String.

En en la ejecución del test, se puede observar de que aunque se envían todos los mensajes sin esperar la ejecución del actor, en realidad estos se encolan. El actor no procesa el siguiente mensaje hasta que no ha terminado la ejecución anterior.

El resultado es que los mensajes son procesados por orden de llegada ya que este actor tiene la implementación por defecto de ‘mailbox‘ (existen otras implementaciones que permiten prioridades de mensajes). Con lo que realiza la espera del primero de ellos y después el eco del resto.

En este test para comprobar el orden de los mensajes que se están ejecutando en el actor se está utilizando un agente de Akka para guardar el estado de manera asíncrona.  El agente almacena la concatenación de los mensajes tipo cadena que se envían al actor.

Para verificar que el orden de los mensajes enviados es el mismo orden de los mensajes que se han procesado se comprueba que estas dos cadenas son iguales.

Cómo funciona un Router

Test: ActorQueueRouteRoundRobinTest.java

Commando maven
mvn  −Dtest=com.logicaalternativa.examples.akka.queue.ActorQueueRouteRoundRobinTest test

Akka permite el concepto de ‘Router‘. Esto permite asociar en una misma referencia de un actor envolver un grupo de actores del mismo tipo.

Se basa en el mismo concepto que tenemos todos en mente de que es un Router: un frontal al que se envían los mensajes y que se encarga de reenviarlos a sus ‘routees‘ para que estos los procesen y devolver la respuesta.

Permite un procesamiento en paralelo mayor, al existir más instancias de un actor, pero con la comodidad de tener una referencia única a la que enviar los mensajes.

[Concepto de router]

Router

Cada miembro del router es de hecho un actor y tiene su propia cola. La estrategia del envío de los mensajes a los miembros del enrutador se define al crear este. En el test de ejemplo se está utilizando la estrategia de envío secuencial de mensajes (‘RoundRobinPool’): se envía un mensaje a un actor del grupo, el siguiente al siguiente actor y así sucesivamente. En la documentación de Akka puedes ver que existen y se pueden utilizar otras estrategias.

El ejemplo levanta un sistema de actores. Se crea un router de 5 actores del tipo ActorNoTypedWhoIam. Este actor tiene una implementación muy sencilla. Se limita a responder en un Map con el mismo mensaje que le envían y su nombre de actor.

Al router se le envían 10 mensajes. En la resolución de cada futuro de respuesta se comprueba que el mensaje es el mismo que se ha enviado. Por otra parte, gracias a la nombre del routee, se guarda la información de que routee ha procesado el mensaje.

Por último se comprueba que a cada actor del grupo del router le ha llegado el mismo número de mensajes (estrategía de envío RoundRobinPool). En este caso son 2 ya que el router lo forman 5 actores y se mandan 10 mensajes.

En las trazas se puede comprobar que el actor con el nombre ‘a‘ le llegan los mensajes ‘0‘ y ‘5‘, al ‘b‘ el ‘1‘ y el ‘6‘ y así sucesivamente.

Como se pueden enviar mensajes entre actores

Test: ActorProxyTest.java

Comando maven
mvn  -Dtest=com.logicaalternativa.examples.akka.message.ActorProxyTest test

Fundamentalmente los actores se pueden enviar mensajes de dos maneras diferentes: tipo ‘fire & forget‘ (dispara y olvida) utilizando la función ‘tell‘ o bien seguir un esquema petición-respuesta utilizando la función ‘ask‘.

Por otra parte un actor puede crear otros actores a los que necesita enviar mensajes. Es muy común que existan actores cuya tarea sea la de orquestar o supervisar el trabajo de los actores que ha creado (‘actores hijos’), y que una parte de esta tarea sea la de reenviar a sus hijos los mensajes que les llegan. Para estos cosos es muy útil utilizar la función ‘forward

El ejemplo se crea un actor ActorNoTypedProxy que crea un actor hijo del tipo ActorNoTypedDummy. Recordemos que este último devuelve un echo de los mensajes que se le envían.

ActorNoTypedProxy hará de proxy para mostrar las diferentes formas de enviar y reenviar mensajes entre actores. Recibe los siguientes mensajes tipo cadena:

forward‘, redirigir el mensaje

Commando maven test 'forward'
mvn -Dtest=com.logicaalternativa.examples.akka.message.ActorProxyTest#testForward test

Cuando recibe un mensaje de este tipo ejecuta llamada al método ‘forward‘ del actor hijo, con el mismo mensaje. El actor hijo (ActorNoTypedDummy) recibirá el mensaje como si el emisor hubiera sido el que emitió el mensaje al actor ActorNoTypedProxy.

redirect‘ dispara y olvida

Comando maven test redirect
mvn -Dtest=com.logicaalternativa.examples.akka.message.ActorProxyTest#testRedirectMessageToChild test

El actor que hace de proxy redirecciona el mensaje llamando a la función ‘tell‘ del hijo indicando que el remitente es el del mensaje original que le llega al proxy. Aquí también el actor hijo (ActorNoTypedDummy) recibirá el mensaje como si el emisor hubiera sido el que emitió el mensaje a ActorNoTypedProxy.

[forward o 'redirect']

forward o ‘redirect’

 ‘future‘, petición-respuesta no bloqueante y asíncrona

Comando maven test 'future'
mvn -Dtest=com.logicaalternativa.examples.akka.message.ActorProxyTest#testFutur test

Este es un ejemplo de comportamiento no bloqueante y asíncrono utilizando los futuros de Scala que ofrece Akka. ActorNoTypedProxy utiliza la función ‘ask‘ para con el mismo mensaje que le ha llegado, preguntar al actor hijo ActorNoTypedDummy y obtener el futuro de la respuesta. Este futuro se envía al remitente del mensaje original.

Al ser este un futuro, si fuera necesario, se puede desde el actor que hace de proxy, operar con él o realizar cualquier tipo de transformación. Por ejemplo se puede registrar un callback, realizar alguna transformación operando con el de manera funcional, etc…

Para simplificar en el test se espera el resultado de la respuesta y como esta también es un futuro, se espera también a este para obtener el resultado.

 ‘await‘, petición-respuesta bloqueante y síncrona

Comando maven test 'await'
mvn -Dtest=com.logicaalternativa.examples.akka.message.ActorProxyTest#testAwaitFutur test

Sigue el mismo esquema que el test anterior pero esperado la respuesta de una manera síncrona.

En el actor proxy realiza la misma llamada ‘ask‘ y a continuación se utiliza la función ‘Await‘ que espera la resolución del futuro. Se obtiene así un comportamiento bloqueante y síncrono de petición-respuesta.


Hasta aquí llega esta primera parte de esta serie de artículos centrados en explicar como funciona Akka con ejemplos. Conceptos como la supervisión entre actores, el bus de eventos y que son los actores tipados serán material de futuras entregas.

Espero que os sirva.

M.E.

Entrada siguiente: Akka por ejemplos. Sobre supervisión, bus de eventos, …