Découverte des acteurs d’Akka en grappe

J’ai essayé de comprendre les concepts d’Akka et des systèmes basés sur les acteurs récemment. Bien que je comprenne assez bien les principes fondamentaux d’Akka, je suis toujours aux sockets avec quelques problèmes de clustering et d’acteurs distants.

Je vais essayer d’illustrer le problème en utilisant l’ exemple de chat WebSocket fourni avec Play Framework 2.0 : il existe un acteur qui contient les WebSockets et qui conserve la liste des utilisateurs actuellement connectés. Les acteurs représentent essentiellement le salon de discussion à la fois techniquement et logiquement. Cela fonctionne parfaitement tant qu’il y a une seule salle de discussion sur un seul serveur.

J’essaie maintenant de comprendre comment cet exemple devrait être étendu lorsque nous parlons de nombreuses salles de discussion dynamics (de nouvelles salles peuvent être ouvertes / fermées à tout moment) s’exécutant sur un cluster de serveurs (avec des nœuds uniques ajoutés ou supprimés). selon la demande actuelle). Dans un tel cas, l’utilisateur A pourrait se connecter au serveur 1 pendant que l’utilisateur B se connecte au serveur 2. Les deux peuvent parler sur la même salle de conversation. Sur chaque serveur, il y aurait toujours un acteur (pour chaque salle de conversation?) Qui contiendrait les instances WebSocket pour recevoir et publier des événements (messages) aux bons utilisateurs. Mais logiquement, il ne devrait y avoir qu’un seul acteur sur le serveur 1 ou le serveur 2 qui contient la liste des utilisateurs actuellement connectés (ou des tâches similaires).

Comment y arriveriez-vous, de préférence en “pure akka” et sans append de système de messagerie supplémentaire comme ZeroMQ ou RabbitMQ?

C’est ce que j’ai imaginé jusqu’à présent, faites-le moi savoir si cela a du sens:

  1. L’utilisateur A se connecte au serveur 1 et un acteur est affecté à son WebSocket.
  2. L’acteur vérifie (à l’aide de Router? EventBus? Autre chose?) Si un “acteur de salle de conversation” pour la salle de discussion active existe sur l’un des nœuds de cluster connectés. Comme il ne le fait pas, il demandera la création d’un nouvel acteur de salle de discussion et enverra et recevra les futurs messages de discussion de / vers cet acteur.
  3. L’utilisateur B se connecte au serveur 2 et un acteur est également affecté à son WebSocket.
  4. Il vérifie également si un acteur de la salle de conversation demandée existe quelque part et le trouve sur le serveur 1.
  5. L’acteur de la salle de discussion sur le serveur 1 fait désormais office de concentrateur pour la salle de conversation donnée, en envoyant des messages à tous les acteurs membres de la discussion «connectés» et en dissortingbuant les messages entrants.

Si le serveur 2 tombe en panne, l’acteur de la salle de discussion devra être recréé sur / déplacé sur le serveur 2 d’une manière ou d’une autre, bien que ce ne soit pas mon souci principal pour le moment. Je m’interroge surtout sur la manière dont cette découverte dynamic des acteurs sur différentes machines, fondamentalement indépendantes, pourrait être réalisée à l’aide des outils d’Akka.

Je regarde la documentation d’Akka depuis un certain temps maintenant, alors peut-être que je manque l’évidence ici. Si oui, veuillez m’éclairer 🙂

Je travaille sur un projet privé qui est fondamentalement une version très étendue de l’exemple du salon de discussion et j’ai également rencontré des problèmes de démarrage avec Akka et l’ensemble de la pensée “décentralisée”. Je peux donc vous dire comment j’ai “résolu” mon forum de discussion étendu:

Je voulais un serveur pouvant être facilement déployé plusieurs fois sans configuration supplémentaire. J’utilise redis comme stockage pour toutes les sessions ouvertes (sérialisation simple de leurs ActorRefs) et pour tous les salons de discussion.

Le serveur a les acteurs suivants:

  • WebsocketSession : qui détient la connexion à un utilisateur et gère les demandes de l’utilisateur et transfère les messages du système.
  • ChatroomManager : il s’agit du diffuseur central, déployé sur chaque instance du serveur. Si un utilisateur souhaite envoyer un message à un salon de discussion, WebSocketSession-Actor envoie toutes les informations au ChatroomManager-Actor, qui diffuse ensuite le message à tous les membres du salon de discussion.

Alors voici ma procédure:

  1. L’utilisateur A se connecte au serveur 1 qui alloue une nouvelle session Websocket. Cet acteur insère le chemin absolu vers cet acteur en redis.
  2. L’utilisateur A rejoint un salon de discussion X qui insère également son chemin absolu (je l’utilise comme identifiant unique d’une session utilisateur) en redis (chaque salon a un ensemble de “connexions”)
  3. L’utilisateur B se connecte au serveur 2 -> redis
  4. L’utilisateur B rejoint le salon de discussion X -> redis
  5. L’utilisateur B envoie un message à la salle de discussion X comme suit: l’utilisateur B envoie son message via le Websocket à son acteur de session qui, après certaines vérifications, envoie un message d’acteur au ChatroomManager. Cet acteur récupère en fait la liste d’utilisateurs de la salle de discussion de redis (chemins absolus utilisés avec la actorFor actorFor d’akka), puis envoie le message à chaque acteur de session. Ces acteurs de session écrivent alors sur leurs websockets.

Dans chaque acteur ChatroomManager, je fais de la mise en cache ActorRef qui donne une vitesse supplémentaire. Je pense que cela diffère de votre approche, surtout que ces ChatroomManagers traitent les requêtes pour tous les salons de discussion. Mais avoir un acteur pour un salon de discussion est un sharepoint défaillance unique que je voulais éviter. En outre, cela causerait-il beaucoup plus de messages, par exemple:

  • L’utilisateur A et l’utilisateur B sont sur le serveur 1.
  • Chatroom X est sur le serveur 2.

Si l’utilisateur A veut parler à l’utilisateur B, ils doivent tous deux communiquer sur l’acteur de salon de discussion sur le serveur 1.

De plus, j’ai utilisé les fonctionnalités d’akka telles que les routeurs (round-robin) -routers pour créer plusieurs instances d’un acteur ChatroomManager sur chaque système afin de gérer de nombreuses requêtes.

Je passe quelques jours à configurer l’infrastructure distante d’akka en combinaison avec la sérialisation et les redis. Mais maintenant, je suis capable de créer autant d’instances de l’application serveur qui utilisent des redis pour y partager ActorRef (sérialisées sous forme de chemins absolus avec ip + port).

Cela peut vous aider un peu plus loin et je suis ouvert à de nouvelles questions (veuillez ne pas parler de mon anglais).

La clé de la montée en charge sur plusieurs machines est de garder l’état mutable aussi isolé que possible. Bien que vous puissiez utiliser un cache dissortingbué pour coordonner l’état de tous les nœuds, vous obtenez des problèmes de synchronisation et de goulot d’étranglement lors du passage à un grand nombre de nœuds. Idéalement, il devrait y avoir un seul acteur connaissant les messages et les participants dans un salon de discussion.

Au cœur de votre problème, si un salon de discussion est représenté par un seul acteur s’exécutant sur une seule machine – ou même si une telle salle existe. L’astuce consiste à acheminer les demandes liées à un salon de discussion donné à l’aide d’un identifiant, tel que le nom de la salle de conversation. Calculez le hachage du nom et, en fonction du nombre, choisissez-en un parmi vos n cases. Le nœud connaîtra ses salles de discussion actuelles et pourra trouver ou créer en toute sécurité l’acteur de salle de conversation qui vous convient.

Vous pouvez jeter un coup d’œil aux articles de blog suivants sur le regroupement et la montée en charge à 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/

J’utiliserais Zookeeper + Norbert pour savoir quels hôtes vont de haut en bas:

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

Désormais, chaque noeud de ma batterie de serveurs de salon de discussion peut connaître tous les hôtes du cluster logique. Ils recevront un rappel lorsqu’un nœud sera déconnecté (ou mis en ligne). N’importe quel nœud peut maintenant conserver une liste sortingée des membres du cluster en cours, hacher l’identifiant du salon de discussion et le mod par la taille de la liste pour obtenir l’index dans la liste qui doit héberger un salon donné. Nous pouvons append 1 et rehash pour choisir un second index (nécessite une boucle jusqu’à ce que vous obteniez un nouvel index) pour calculer le deuxième hôte à contenir une deuxième copie du salon de discussion pour la redondance. Sur chacun des deux hôtes de chatroom se trouve un acteur de chatroom qui transmet simplement tous les messages de chat à chaque acteur Websocket qui est un membre de chatroom.

Nous pouvons désormais envoyer des messages de discussion via les deux acteurs actifs du salon de discussion avec un routeur Akka personnalisé. Un client envoie simplement le message une fois et le routeur exécute les modifications de hachage et les envoie aux deux acteurs du salon de discussion distant. Je voudrais utiliser l’algorithme de flocon de neige Twitter pour générer des identifiants uniques de 64 bits pour les messages envoyés. Voir l’algorithme dans la méthode nextId () du code sur le lien suivant. Les parameters datacenterId et workerId peuvent être définis à l’aide des propriétés norbert pour garantir qu’aucun ID de collision n’est généré sur les différents serveurs:

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

Désormais, deux copies de chaque message iront à chaque sharepoint terminaison client via chacun des deux acteurs actifs du salon de discussion. Sur chaque acteur client Websocket, je masquerais les identifiants de flocon de neige pour connaître le numéro datacenterId + workerId envoyant le message et garder la trace du numéro de message de chat le plus élevé vu de chaque hôte du cluster. Ensuite, j’ignorerais tous les messages qui ne sont pas supérieurs à ce qui a déjà été vu sur le client donné pour un hôte expéditeur donné. Cela dédupliquerait la paire de messages provenant des deux acteurs actifs de la salle de discussion.

Jusqu’ici tout va bien; Nous aurions une messagerie résiliente, car si un nœud meurt, nous ne perdrons pas la copie restante des salons de discussion. Les messages circuleront automatiquement via le deuxième salon de discussion.

Ensuite, nous devons traiter les noeuds qui quittent le cluster ou sont réintroduits dans le cluster. Nous allons obtenir un rappel norbert dans chaque nœud pour nous informer des changements d’appartenance au cluster. Sur ce rappel, nous pouvons envoyer un message Akka via le routeur personnalisé en indiquant la nouvelle liste d’adhérents et le nom d’hôte actuel. Le routeur personnalisé sur l’hôte en cours verra ce message et mettra à jour son état pour connaître la nouvelle appartenance au cluster afin de calculer la nouvelle paire de noeuds pour envoyer le trafic d’un salon de discussion donné. Cet accusé de réception de la nouvelle appartenance au cluster sera envoyé par le routeur à tous les nœuds afin que chaque serveur puisse suivre le moment où tous les serveurs ont rattrapé le changement d’adhésion et envoient désormais correctement des messages.

Le salon de discussion qui a survécu peut encore être actif après le changement d’adhésion. Dans ce cas, tous les routeurs de tous les nœuds continueront à l’envoyer normalement, mais enverront également un message spéculatif au nouvel hôte de la deuxième salle de discussion. Ce deuxième salon de discussion n’est peut-être pas encore disponible, mais ce n’est pas un problème car les messages circuleront via le survivant. Si le salon de discussion restant n’est plus actif après la modification de l’adhésion, tous les routeurs de tous les hôtes enverront d’abord trois hôtes; le survivant et les deux nouveaux nœuds. Le mécanisme de surveillance de la mort akka peut être utilisé afin que tous les nœuds puissent éventuellement voir l’arrêt de la salle de chat qui a survécu pour revenir au routage du trafic de discussion via deux hôtes.

Ensuite, nous devons migrer le salon de discussion du serveur survivant sur le ou les nouveaux hôtes en fonction des circonstances. Le comédien de la chasortingng va à un moment donné recevoir un message l’informant de la nouvelle adhésion au cluster. Il commencera par envoyer une copie de l’appartenance à la chatroom aux nouveaux nœuds. Ce message créera la nouvelle copie de l’acteur de discussion avec l’adhésion correcte sur les nouveaux noeuds. Si le survivant n’est plus l’un des deux nœuds qui doivent contenir la salle de discussion, il passera en mode de mise hors service. En mode de mise hors service, il ne transmettra les messages aux nouveaux noeuds primaires et secondaires qu’aux membres du forum. Le transfert de messages Akka est parfait pour cela.

Un salon de désaffectation sera à l’écoute des messages d’accusé de réception d’adhésion au cluster norbert de chaque nœud. Finalement, il verra que tous les nœuds du cluster ont reconnu la nouvelle appartenance au cluster. Il sait alors qu’il ne recevra plus aucun message à transmettre. Il peut alors se tuer. Akka hotswapping est parfait pour mettre en œuvre le comportement de démantèlement.

Jusqu’ici tout va bien; Nous avons une configuration de messagerie résiliente qui ne perdra pas de messages pour une panne de nœud. Au moment où l’appartenance au cluster change, le trafic intranode augmente pour copier les salons de discussion vers de nouveaux nœuds. Nous avons également une vague résiduelle de transfert intranode de messages vers les nœuds jusqu’à ce que tous les serveurs aient rattrapé les serveurs qui ont déplacé deux serveurs. Si nous voulons faire évoluer le système, nous pouvons attendre un point bas dans le trafic des utilisateurs et activer un nouveau nœud. Les salons de discussion seraient automatiquement redissortingbués sur les nouveaux nœuds.

La description ci-dessus est basée sur la lecture de l’article suivant et sur sa traduction en concepts Akka:

https://www.dropbox.com/s/iihpq9bjcfver07/VLDB-Paper.pdf