Quand n'est-il pas bon d'utiliser des acteurs dans akka / erlang?

58

Je travaille avec Akka depuis 7 ou 8 mois maintenant. Lorsque j'ai commencé, je travaillais sur des applications et je remarquais que les acteurs étaient utilisés pratiquement n'importe où une fois dans le système d'acteurs pour communiquer entre la plupart des objets. Alors j'ai fait la même chose - actionner un autre acteur pour x / y / z.

Il me semble que c'est peut-être trop aveugle, ce qui ajoute à la complexité là où ce n'est pas nécessaire - mais je ne trouve aucune discussion sur le point de savoir où les acteurs par rapport à la logique synchrone ou même asynchrone via des futurs devraient être utilisés. J'ai commencé à réfléchir à ma position après que mon collègue ait mentionné quelque chose de similaire. J'ai récemment réalisé plusieurs cas dans lesquels j'avais réfléchi à une tâche, puis évité de créer un autre acteur, car je pouvais obtenir le même résultat en toute sécurité dans une implémentation immuable - par exemple, obtenir des valeurs de configuration à partir d'une base de données ou d'un fichier où vous accédez très rarement et restez accessible. attendre le résultat est le cas d'utilisation réel.

En particulier, il me semble que dans tous les cas où vous jouez avec un état immuable, les acteurs créent de la complexité et limitent le débit - une fonction pure dans un objet, par exemple, peut être appelée simultanément sans risque, quel que soit le niveau de simultanéité. un acteur ne peut traiter qu'un message à la fois. L'autre considération à prendre en compte est de laisser filer le fil si vous devez attendre le résultat, sauf si vous commencez à utiliser des futures, mais dans les cas où vous n'avez pas à vous soucier de la messagerie asynchrone ou de l'échelle, il semble exagéré d'employer un acteur.

Ma question est donc la suivante: y at-il un mauvais moment pour utiliser des acteurs? Je suis curieux de voir à quoi ressemble Erlang et aimerais vraiment la perspicacité des autres. Ou s'il y a des principes autour de l'utilisation par les acteurs.

JasonG
la source
Quel type de travail donne l'occasion de travailler avec Akka tous les jours pendant des mois?
Den
3
Bons. :) Ou faites le changement vous-même.
JasonG
Je voudrais savoir précisément quels sont les compromis entre askun acteur et une simple plaine Future.
Max Heiber
2
Quelques années plus tard, j’ai écrit un livre sur Akka et je pense que vous pouvez le résumer ainsi: Si vous avez un état - par exemple un compteur - plusieurs threads qui lisent et écrivent à partir de / vers cette valeur finiront par se piétiner. sans synchronisation et verrouillage. Si vous placez cet état dans un acteur, vous pouvez soudainement être sûr qu'il est accessible en toute sécurité et que vous ne laissez pas tomber des écritures ou des lectures obsolètes. Les acteurs d'Erlang et d'Akka supposent également que l'état peut se dégrader et que l'acteur peut commettre des erreurs afin que vous disposiez de certaines caractéristiques du système d'auto-guérison. Les futures sont plus simples si vous n'avez pas besoin de muter
JasonG
Bien des années plus tard, je suis un programmeur erlang / elixir et j'y reviens sans cesse avec de nouvelles connaissances :) Elixir est assez différent, car il n'y a pas d'objet comme alternative aux processus, les processus sont toujours construits lorsque vous avez besoin d'un état . Il se trouve que c'est juste simultané. C'est la meilleure heuristique encore.
JasonG

Réponses:

23

C'est une question qui m'intéresse et sur laquelle j'ai effectué des recherches. Pour d'autres points de vue, consultez ce billet de blog de Noel Walsh ou cette question sur Stack Overflow. J'ai quelques opinions que j'aimerais offrir:

  • Je pense qu'Akka, parce que cela fonctionne avec les messages, encourage un "état d'esprit poussé". Souvent, pour la concurrence, je dirais que ce n’est pas ce que vous voulez. Tirer est beaucoup plus sûr. Par exemple, un modèle courant pour les systèmes distribués consiste à avoir un ensemble de travailleurs traitant des informations dans une file d'attente . Cela est évidemment possible à Akka, mais cela ne semble pas nécessairement être la première approche que les gens essaient . Akka propose également des boîtes aux lettres durables, mais là encore, cela dépend de la façon dont vous les utilisez: une seule file d'attente partagée est beaucoup plus flexible que les files d'attente par utilisateur pour équilibrer / réaffecter le travail.
  • Il est facile d'entrer dans la mentalité de remplacer vos classes par des acteurs. En fait, certaines personnes semblent même le préconiser en disant que les acteurs ne devraient faire qu'une chose . Comme le décrit Jason, la complexité du code augmente la complexité du code, car si chaque classe est un acteur, il y a beaucoup de messages supplémentaires et de blocs de réception / envoi. Cela rend également plus difficile la compréhension et le test du code car vous perdez la formalité des interfaces - et je ne suis pas convaincu que les commentaires constituent une solution à ce problème . En outre, malgré l'efficacité légendaire d'Akka , je soupçonne que la prolifération d'acteurs n'est pas une bonne idée en termes de performances: lorsque nous utilisons des threads Java, nous savons qu'ils sont précieux et les conservons en conséquence.
  • C'est lié au point précédent, mais un autre inconvénient est la perte d'informations de type que Noel et Pino soulignent, comme c'est le cas pour beaucoup d'entre nous, c'est pourquoi nous utilisons Scala plutôt que d'autres langages tels que Python. Il y a quelques moyens de contourner ce problème, mais ils sont non standard , non recommandés ou expérimentaux .
  • Enfin, la concurrence, même si vous avez un état d'esprit "laissez-le planter" , est difficile. Les modèles de programmation alternatifs peuvent aider, mais ils ne font pas disparaître les problèmes - ils les modifient - c'est pourquoi il est bon de penser officiellement à eux . C'est également la raison pour laquelle les développeurs de Joe Average recherchent des outils prêts à l'emploi tels que les bases de données RabbitMQ, Storm, Hadoop, Spark, Kafka ou NoSQL. Akka possède des outils et des composants prédéfinis, ce qui est cool, mais le niveau de sécurité est également assez bas. Par conséquent, des éléments communs mieux préparés des systèmes distribués aideraient les développeurs et garantiraient que les systèmes sont correctement construits.

Comme Jason, je suis impatient d'entendre les idées des autres ici. Comment puis-je résoudre certains des problèmes ci-dessus et utiliser Akka mieux?

Mark Butler
la source
1
J'ai trouvé cet article sur les tests à Akka timgilbert.wordpress.com/2013/07/07/…
Mark Butler
"Composition sur héritage" est toujours utile pour l'injection de dépendance. Par exemple, transmettez la dépendance en tant que props (utilise les paramètres du constructeur). Le problème serait alors la supervision: l'acteur créé serait supervisé ailleurs que l'acteur. Un routeur pourrait être utilisé pour intégrer une stratégie de supervision autour d’acteurs créés au plus haut niveau, mais sacrément, c’est plutôt complexe maintenant! Je suppose que le gâteau serait une meilleure solution - avoir un trait avec la création d’acteur pour un acteur de service de base de données par exemple. Ensuite, utilisez simplement un trait de service dactylographie / stub de base de test dans un contexte textuel.
JasonG
1
Oui, exactement - la supervision ne fonctionne pas bien avec l'injection de dépendance. J'ai également eu des cas où il était nécessaire d'injecter une usine plutôt que la classe, sinon il y avait des problèmes de fermeture étranges - je devine à cause du traitement des fils par Akka.
Mark Butler
Je pense que ce n’est pas une mauvaise idée - merci Mark - je pourrais effectivement essayer d’écrire un peu à ce sujet et de le socialiser. Vous pourriez injecter quelque chose qui donne les accessoires peut-être? Une usine d'acteurs de toutes sortes où le consommateur peut instancier l'acteur. Par exemple, def méthode (arg) = renvoie les accessoires (new ActorWithArgs (arg)) de cette fabrique (psuedo?) afin que l'acteur soit créé dans le bon contexte. Cela semble être une bonne idée et une bonne cible pour des solutions de gâteau pour di aussi.
JasonG
Je viens de commencer à suivre ce cours: coursera.org/course/posa Bien qu'il soit principalement destiné aux personnes programmant Android, il offre également une bonne vue d'ensemble de la simultanéité en Java. Je me pose donc la question suivante: "Akka n’est-il pas une forme sophistiquée de boucles d’événements avec quelques cloches et de sifflets (car vous pouvez placer des boucles d’événements sur différents threads)?"
Mark Butler
21

Cela vaut la peine d’examiner à quoi sert le modèle d’acteur: le modèle d’acteur est

  1. un modèle de concurrence
  2. qui évite l'accès simultané à l'état mutable
  3. utilisant des mécanismes de communication asynchrones pour assurer la simultanéité.

Cela est utile car l'utilisation de l'état partagé à partir de plusieurs threads devient très difficile, en particulier lorsqu'il existe des relations entre différents composants de l'état partagé qui doivent être maintenues synchronisées. Cependant, si vous avez des composants de domaine dans lesquels:

  • Vous n'autorisez pas la concurrence, OU
  • Vous n'autorisez pas l'état mutable (comme dans la programmation fonctionnelle), OU
  • Vous devez compter sur un mécanisme de communication synchrone,

alors le modèle d'acteur ne fournira pas beaucoup (le cas échéant) d'avantages.

J'espère que ça t'as aidé.

Aidan Cully
la source
Merci - je suppose que vous devez vraiment évaluer deux choses: l’état mutable et la simultanéité? Aucun problème à résoudre si une classe est sans état OU pas concurrente. Une autre considération - je pense que la tolérance aux pannes est un autre point? Par exemple, si vous avez un client Redis immuable mais que celui-ci peut échouer lors de l'exécution, cela ne serait-il pas un bon cas d'utilisation? que le redémarrage de cet acteur peut être nécessaire? En tant que - bien que la classe puisse être purement immuable - il est très possible qu’il y ait un état corrompu extérieur à l’acteur immuable qui peut provoquer son échec.
JasonG
Je suppose que l'acteur responsable de la communication avec Redis obtiendrait l'exception et redémarrerait bien.
JasonG
@JasonG À mon sens, la "tolérance aux pannes" n'est pas un aspect du modèle d'acteur en général - c'est quelque chose qui vient avec la mise en œuvre du modèle d'acteur à Erlang (et peut-être Akka?). Je ne peux pas parler à Redis, même si je dois admettre que "client immuable" me semble étrange ... Si un composant logiciel peut échouer, je ne vois pas en quoi il peut être considéré comme immuable.
Aidan Cully
La tolérance aux pannes et la supervision existent dans akka et cela ressemble beaucoup à erlang. Je comprends ce que vous dites à propos de client immuable, mais immuable signifie que l’état du code ne change pas. Si j'initialise une connexion au démarrage, il est possible que le code client soit immuable avec une stratégie de redémarrage utilisée pour tout type d'erreur, en réinitialisant simplement l'acteur en cas de problème.
JasonG
12

Votre intuition est correcte, à mon humble avis. Utiliser des acteurs partout, c'est comme avoir le marteau proverbial et ne voir que des clous.

La meilleure pratique d'Erlang consiste à utiliser des processus / acteurs pour toutes les activités qui se déroulent simultanément. C'est comme dans la vraie vie. Il est parfois difficile de trouver la granularité appropriée, mais la plupart du temps, il suffit de regarder le domaine modélisé et d’utiliser un peu de bon sens. J'ai bien peur de ne pas avoir de meilleure méthode que celle-là, mais j'espère que cela aidera.

Vlad Dumitrescu
la source
Cool merci. Je suis vraiment intéressé de voir ce qui se passe dans cette discussion.
JasonG
0

Dans l'ordre, la messagerie d'entrée / de sortie:

J'ai récemment rencontré une application basée sur Akka dans laquelle le modèle d'acteur posait des problèmes de concurrence, un modèle plus simple aurait suffi mieux sous charge.

Le problème était que les messages entrants se déplaçaient dans des «voies» différentes (via des chemins d'acteurs différents), mais le code supposait que les messages arriveraient à leur destination finale dans le même ordre. Tant que les données arrivaient avec des intervalles suffisamment grands, cela fonctionnait, car il n'y aurait qu'un seul message contradictoire à destination de la destination. Lorsque les intervalles ont diminué, ils ont commencé à arriver en panne et à provoquer un comportement étrange.

Le problème aurait pu être résolu correctement avec un peu moins d'acteurs, mais c'est une erreur facile à commettre lorsque vous en abusez.

DHa
la source
Ceci est fondamental et largement pertinent. Vous ne pouvez garantir la commande entre deux acteurs. doc.akka.io/docs/akka/current/scala/general/… Vous ne pouvez pas garantir la livraison des commandes quel que soit le système sur lequel vous vous inscrivez . andreasohlund.net/2012/01/18/dont-assume-message-ordering
JasonG Le
-1

À mon avis, il existe deux cas d'utilisation pour les acteurs. Ressources partagées telles que les ports, etc., et grands états. Le premier a été bien couvert par la discussion jusqu'à présent, mais un grand État est également une raison valable.

Une grande structure passée avec chaque appel de procédure peut utiliser beaucoup de pile. Cet état peut être placé dans un processus séparé, la structure remplacée par un identifiant de processus et ce processus interrogé au besoin.

Les bases de données telles que mnesia peuvent être considérées comme un état de stockage externe au processus d'interrogation.

tony wallace
la source
1
Pouvez-vous clarifier? Voulez-vous dire grand état à travers le réseau? Dans un processus local, le passage d'une référence à une structure immuable n'a presque aucun coût et vous ne pouvez pas passer de structures mutables. En supposant que vous parlez d'une grande structure mutable qui doit être copiée pour être envoyée? Donc, c'est à nouveau fondamentalement la même chose - un état partagé. La taille ne change vraiment rien. Faites-moi savoir si je me trompe.
JasonG
Alors, comment passez-vous par référence? Pour ce faire, il faut certainement mettre la structure en processus et réussir le processus. Les appels locaux vont mettre les données sur la pile et chaque appel récursif le fera à nouveau (à l'exception de la récursion finale). Le traitement récursif de la structure peut être effectué à l'aide de listes permettant de transférer l'état d'un appel à l'autre, cet état pouvant ensuite référencer la structure dans l'autre processus.
tony wallace