playframework playframework-2.0 akka akka-cluster

playframework - Descubrimiento de los actores akka en grupo



playframework-2.0 akka-cluster (3)

He estado tratando de envolver mi cabeza en torno a los conceptos de Akka y los sistemas basados ​​en actores recientemente. Si bien ya tengo un buen conocimiento de los fundamentos de Akka, todavía estoy luchando con algunas cosas cuando se trata de agrupaciones y actores remotos.

Intentaré ilustrar el problema utilizando el ejemplo de chat de WebSocket que viene con Play Framework 2.0 : hay un actor que posee los WebSockets y que mantiene una lista de los usuarios conectados actualmente. Básicamente, los actores representan la sala de chat tanto técnica como lógicamente. Esto funciona perfectamente bien siempre que haya una única sala de chat en un solo servidor.

Ahora estoy tratando de entender cómo este ejemplo tendría que extenderse cuando hablamos de muchas salas de chat dinámicas (las nuevas salas se pueden abrir / cerrar en cualquier momento) que se ejecutan en un grupo de servidores (con nodos únicos que se agregan o eliminan Según demanda actual). En tal caso, el usuario A podría conectarse al servidor 1 mientras que el usuario B se conecta al servidor 2. Ambos pueden estar hablando en la misma sala de chat. En cada servidor todavía habría un actor (¿para cada sala de chat?) Que contiene las instancias de WebSocket para recibir y publicar eventos (mensajes) para los usuarios correctos. Pero lógicamente, solo debe haber un actor de sala de chat en el servidor 1 o en el servidor 2 que contenga la lista de usuarios actualmente conectados (o tareas similares).

¿Cómo haría para lograr esto, preferiblemente en "puro akka" y sin agregar un sistema de mensajería adicional como ZeroMQ o RabbitMQ?

Esto es lo que he encontrado hasta ahora, por favor, hágamelo saber si tiene algún sentido:

  1. El usuario A se conecta al servidor 1 y se asigna un actor que tiene su WebSocket.
  2. El actor verifica (usando Router? EventBus? ¿Algo más?) Si existe un "actor de sala de chat" para la sala de chat activa en cualquiera de los nodos del clúster conectado. Como no lo hace, solicitará la creación de un nuevo actor de sala de chat de alguna manera y enviará y recibirá futuros mensajes de chat a / de este actor.
  3. El usuario B se conecta en el servidor 2 y también se asigna un actor para su WebSocket.
  4. También comprueba si un actor para la sala de chat solicitada existe en algún lugar y lo encuentra en el servidor 1.
  5. El actor de la sala de chat en el servidor 1 ahora actúa como el centro de la sala de chat dada, enviando mensajes a todos los actores miembros del chat "conectados" y distribuyendo los entrantes.

Si el servidor 2 falla, el actor de la sala de chat tendría que volver a crearse / trasladarse al servidor 2 de alguna manera, aunque esta no es mi principal preocupación en este momento. Lo que más me pregunto es cómo se podría hacer este descubrimiento dinámico de actores sobre varias máquinas básicamente independientes utilizando el conjunto de herramientas de Akka.

He estado mirando la documentación de Akka desde hace bastante tiempo, así que tal vez me esté perdiendo lo obvio aquí. Si es así, por favor ilumíname :-)


Estoy trabajando en un proyecto privado que es básicamente una versión muy extendida del ejemplo de sala de chat y también tuve problemas de inicio con akka y todo el pensamiento "descentralizado". Entonces puedo decirte cómo "resolví" mi sala de chat extendida:

Quería un servidor que pudiera desplegarse fácilmente varias veces sin mucha configuración adicional. Estoy usando redis como almacenamiento para todas las sesiones de usuario abiertas (simple serialización de sus ActorRefs) y para todas las salas de chat.

El servidor tiene los siguientes actores:

  • WebsocketSession : que mantiene la conexión con un usuario y maneja las solicitudes del usuario y reenvía los mensajes del sistema.
  • ChatroomManager : esta es la emisora ​​central, que se implementa en cada instancia del servidor. Si un usuario desea enviar un mensaje a una sala de chat, WebSocketSession-Actor envía toda la información a ChatroomManager-Actor, que luego transmite el mensaje a todos los miembros de la sala de chat.

Así que aquí está mi procedimiento:

  1. El usuario A se conecta al servidor 1 que asigna una nueva WebsocketSession. Este actor inserta el camino absoluto hacia este actor en redis.
  2. El usuario A se une a una sala de chat X que también inserta su ruta absoluta (uso esto como el ID único de una sesión de usuario) en redis (cada sala de chat tiene un conjunto de "conexiones")
  3. El usuario B se conecta al servidor 2 -> redis
  4. El usuario B se une a la sala de chat X -> redis
  5. El usuario B envía un mensaje a la sala de chat X de la siguiente manera: el usuario B envía su mensaje a través del Websocket a su actor de sesión, el cual (después de algunas verificaciones) envía un mensaje de actor al ChatroomManager. Este actor realmente recupera la lista de usuarios de la sala de chat de redis (rutas absolutas utilizadas con el método actorFor de akka) y luego envía el mensaje a cada actor de sesión. Estos actores de sesión escriben en sus websockets.

En cada ChatroomManager-actor, hago un ActorRef almacenamiento en caché ActorRef que le dio velocidad adicional. Creo que esto difiere de su enfoque, especialmente que estos ChatroomManagers manejan las solicitudes de todas las salas de chat. Pero tener un actor para una sala de chat es un único punto de falla que quería evitar. Además esto causaría muchos más mensajes, por ejemplo:

  • El usuario A y el usuario B están en el servidor 1.
  • Chatroom X está en el servidor 2.

Si el usuario A quiere hablar con el usuario B, ambos deberían comunicarse a través del chatroom-actor en el servidor 1.

Además, utilicé funcionalidades de akka como los viajeros (round-robin) para crear múltiples instancias del actor ChatroomManager en cada sistema para manejar muchas solicitudes.

Pasé algunos días configurando toda la infraestructura remota de akka en combinación con serialización y redis. Pero ahora puedo crear cualquier número de instancias de la aplicación del servidor que usan redis para compartir ActorRef s (serializadas como rutas absolutas con el puerto ip +).

Esto puede ayudarlo un poco más y estoy abierto a nuevas preguntas (por favor, no sobre mi inglés;).


La clave para escalar a través de múltiples máquinas es mantener el estado mutable lo más aislado posible. Aunque puede usar una memoria caché distribuida para coordinar el estado en todos los nodos, esto le brindaría sincronización y problemas de cuellos de botella al escalar a una gran cantidad de nodos. Idealmente, entonces, debería haber un solo actor que conozca los mensajes y los participantes en una sala de chat.

El núcleo de su problema es si una sala de chat está representada por un solo actor que se ejecuta en una sola máquina, o incluso si tal sala existe. El truco consiste en enrutar las solicitudes relacionadas con una sala de chat determinada mediante un identificador, como el nombre de la sala de chat. Calcule el hash del nombre y, según el número, elija uno de sus n recuadros. El nodo sabrá acerca de sus salas de chat actuales y podrá encontrar o crear de forma segura el actor de sala de chat correcto para usted.

Puede echar un vistazo a las siguientes publicaciones del blog que tratan sobre el agrupamiento y la ampliación en Akka:

http://blog.softmemes.com/2012/06/16/clustered-akka-building-akka-2-2-today-part-1/

http://blog.softmemes.com/2012/06/16/clustered-akka-building-akka-2-2-today-part-2/


Usaría Zookeeper + Norbert para saber qué hosts están subiendo y bajando:

http://www.ibm.com/developerworks/library/j-zookeeper/

Ahora, todos los nodos de la granja de servidores de mi sala de chat pueden conocer todos los hosts del clúster lógico. Reciben una devolución de llamada cuando un nodo se desconecta (o se conecta). Ahora, cualquier nodo puede mantener una lista ordenada de los miembros actuales del clúster, marcar el ID de la sala de chat y modificar por el tamaño de la lista para obtener el índice dentro de la lista, que es el nodo que debe albergar cualquier sala de chat determinada. Podemos agregar 1 y repetir para seleccionar un segundo índice (requiere un bucle hasta que obtenga un índice nuevo) para calcular el segundo host para mantener una segunda copia de la sala de chat para redundancia. En cada uno de los dos anfitriones de la sala de chat hay un actor de la sala de chat que reenvía todos los mensajes de chat a cada actor de Websocket que es miembro de la sala de chat.

Ahora podemos enviar mensajes de chat a través de los dos actores activos de la sala de chat con un enrutador Akka personalizado. Un cliente simplemente envía el mensaje una vez y el enrutador realizará las modificaciones hash y las enviará a los dos actores de la sala de chat remota. Yo usaría el algoritmo de copo de nieve de Twitter para generar identificaciones únicas de 64 bits para los mensajes que se envían. Vea el algoritmo en el método nextId () del código en el siguiente enlace. El datacenterId y workerId se pueden configurar utilizando las propiedades de norbert para garantizar que no se generen ID en conflicto en los diferentes servidores:

https://github.com/twitter/snowflake/blob/master/src/main/scala/com/twitter/service/snowflake/IdWorker.scala

Ahora, dos copias de cada mensaje irán a cada punto final del cliente a través de cada uno de los dos actores activos de la sala de chat. En cada actor cliente de Websocket, desenmascara las identificaciones de los copos de nieve para conocer el número de ID de centro de datos + workerId que envía el mensaje y realiza un seguimiento del número de mensaje de chat más alto visto desde cada host en el clúster. Entonces ignoraría cualquier mensaje que no sea superior a lo que ya se ha visto en el cliente dado para un host de remitente dado. Esto deduplicaría el par de mensajes que llegan a través de los dos actores activos de la sala de chat.

Hasta ahora tan bueno; Tendríamos mensajes resilientes en el sentido de que, si un nodo muere, no perderemos la única copia sobreviviente de las salas de chat. Los mensajes fluirán de forma ininterrumpida a través de la segunda sala de chat automáticamente.

A continuación, debemos tratar con los nodos que abandonan el clúster o que se vuelven a agregar al clúster. Obtendremos una devolución de llamada de norbert dentro de cada nodo para notificarnos sobre los cambios en la membresía del clúster. En esta devolución de llamada, podemos enviar un mensaje akka a través del enrutador personalizado que indica la nueva lista de miembros y el nombre de host actual. El enrutador personalizado en el host actual verá ese mensaje y actualizará su estado para conocer la nueva membresía del clúster para calcular el nuevo par de nodos a través del cual se envía el tráfico de cualquier sala de chat. El enrutador enviará este acuse de recibo de la nueva pertenencia al clúster a todos los nodos, de modo que cada servidor pueda realizar un seguimiento cuando todos los servidores hayan alcanzado el cambio de membresía y ahora estén enviando mensajes correctamente.

La sala de chat sobreviviente puede estar activa después del cambio de membresía. En cuyo caso, todos los enrutadores de todos los nodos continuarán enviándolo de manera normal, pero también enviarán un mensaje especulativo al nuevo segundo anfitrión de sala de chat. Es posible que la segunda sala de chat aún no esté activa, pero eso no es un problema ya que los mensajes fluirán a través del sobreviviente. Si la sala de chat sobreviviente ya no está activa después de que la membresía cambie, todos los enrutadores de todos los hosts se enviarán primero a tres hosts; El sobreviviente y los dos nuevos nodos. El mecanismo de vigilancia de la muerte de akka se puede utilizar para que todos los nodos puedan ver el cierre de la sala de chat sobreviviente para volver al tráfico de chat de enrutamiento a través de dos hosts.

A continuación, debemos migrar la sala de chat del servidor sobreviviente a uno o dos hosts nuevos según las circunstancias. El actor de la sala de chat de Surving recibirá en algún momento un mensaje que le informará sobre la nueva membresía del clúster. Comenzará enviando una copia de la membresía de la sala de chat a los nuevos nodos. Este mensaje creará la nueva copia del actor de la sala de chat con la membresía correcta en los nuevos nodos. Si el sobreviviente ya no es uno de los dos nodos que deben estar en la sala de chat, entrará en modo de cierre. En el modo de cierre de servicio, solo reenviará los mensajes a los nuevos nodos primario y secundario, no a los miembros de la sala de chat. El reenvío de mensajes Akka es perfecto para esto.

Una sala de chat de desmantelamiento escuchará los mensajes de acuse de recibo de membresía de norbert cluster desde cada nodo. Eventualmente verá que todos los nodos dentro del clúster han reconocido la nueva pertenencia al clúster. Entonces sabe que ya no recibirá más mensajes para reenviar. Entonces puede matarse a sí mismo. Akka hotswapping es perfecto para implementar el comportamiento de desmantelamiento.

Hasta ahora tan bueno; tenemos una configuración de mensajería resistente que no perderá los mensajes en caso de una caída del nodo. En el punto en el que la membresía del clúster cambia, obtendremos un aumento en el tráfico del intranodo para copiar salas de chat a nuevos nodos. También tenemos una avalancha residual de reenvío de intranodos de mensajes a nodos hasta que todos los servidores se han puesto al día con qué salas de chat han movido dos servidores. Si queremos escalar el sistema, podemos esperar hasta un punto bajo en el tráfico de usuarios y simplemente activar un nuevo nodo. Las salas de chat se redistribuirían automáticamente a través de los nuevos nodos.

La descripción anterior se basa en leer el siguiente documento y traducirlo en conceptos akka:

dropbox.com/s/iihpq9bjcfver07/VLDB-Paper.pdf