Anti-modèles Akka: trop d'acteurs

image

Par akka, il y a peu de matériaux sur Habré. J'ai décidé de traduire certains des contre-modèles décrits par Manuel sur son blog. Ils peuvent ne pas vraiment être évidents pour les personnes qui ont rencontré le cadre pour la première fois.

Il m'est venu à l'esprit que je n'avais pas encore écrit sur cet anti-pattern très fréquent. On le retrouve souvent dans le code des développeurs qui commencent tout juste à travailler avec le modèle d'acteur.

Il y a deux façons d'obtenir trop d'acteurs:

- avoir développé un système avec trop de types d'acteurs différents, dont beaucoup ne sont pas nécessaires
- créer un très grand nombre d'acteurs en runtime quand ce n'est pas nécessaire et inefficace

Examinons ces options en détail.

Trop de types d'acteurs


L'idée générale est quelque chose comme ceci: "nous avons des acteurs, donc tout devrait être un acteur."

Le modèle d'acteur simplifie l'écriture d'applications asynchrones. Pour ce faire, il donne l'illusion de l'exécution de code synchrone à l'intérieur d'un acteur - il n'est pas nécessaire de se soucier de l'accès simultané à l'état d'un acteur, car seul un acteur peut accéder à son état, et les messages sont traités un à la fois, tour à tour.

Mais en fait, tout ne doit pas être fait de manière asynchrone. Les appels à des méthodes qui sont exclusivement associées au CPU (et qui ne sont pas "bloqués" dans le sens où ils ne surchargent pas complètement le CPU, par exemple, en calculant la valeur Pi), ne doivent pas être effectués de manière asynchrone.

Je vois assez souvent du code avec un grand nombre d'acteurs différents interagissant entre eux et ne faisant rien, ce qui présente un grand avantage dans l'exécution asynchrone ou simultanée. Dans ces projets, le même état doit être stocké par chacun de ces acteurs ou leur être transmis dans chaque message.

Cette approche présente deux inconvénients:

"Vous n'obtenez rien en termes de performances." Au contraire, il y a des frais généraux associés à la création de messages et à leur transmission.
- Avec chaque type d'acteur et les messages associés, le système devient plus difficile à comprendre et à maintenir.

Par conséquent, lors de la conception de systèmes d'acteurs, vous devez penser à ce qui devrait être réellement asynchrone, essentiellement ceci:

- appels vers des systèmes externes (en dehors de votre jvm)
- appels à des opérations de blocage (API obsolètes, informatique lourde, ...)

Trop d'acteurs à l'exécution


L'idée générale est quelque chose comme ceci: "plus nous avons d'acteurs, plus tout ira vite."

En fait, les acteurs sont légers et vous pouvez en exécuter des millions sur une seule machine virtuelle . Oui tu peux. Mais est-ce nécessaire?

image

Si vous le pouvez, cela ne signifie pas que vous devriez

Réponse courte: pas toujours - cela dépend de ce que vous faites avec les acteurs.

Si votre système a de nombreux acteurs à longue durée de vie, chacun contenant un petit état, et interagissant de temps en temps, vous pourriez bien être avec un million d'acteurs - et c'est un cas d'utilisation légitime, très bien soutenu par Akka. Par exemple, vous pouvez créer un système avec un grand nombre d'utilisateurs, où chaque utilisateur est représenté par un acteur. Un acteur pur Akka ne prend que 300 octets de mémoire, il est donc tout à fait possible de créer des millions sur une machine et de les laisser travailler sans se soucier de rien. Et si à la fin vous créez beaucoup d'acteurs ou d'acteurs avec une grande chance qu'ils ne rentreront plus dans la mémoire d'une machine, le partage de cluster simplifiera la distribution des acteurs sur plusieurs machines.

Cependant, si plusieurs types d'acteurs sont impliqués dans le calcul de quelque chose - par exemple, l'analyse d'un document XML - il est douteux de créer des millions de tels acteurs (cela n'a pas d'importance directement ou via un routeur).

Le processeur dispose d'un nombre fixe de cœurs (threads matériels) et les acteurs Akka traitent les messages dans un ExecutionContext basé sur un pool de threads. Par défaut, il s'agit de fork-join-executor basé sur ForkJoinPool ajouté dans Java 7.

Mais, malgré son avantage technique, forkjoinpool n'est pas une magie qui abroge les lois de la physique. Si vous avez un million d'acteurs, chacun analysant un document XML (déjà chargé en mémoire) et 4 threads matériels, le système ne fonctionnera pas beaucoup mieux que si vous n'aviez que 4 acteurs qui analysent ces documents XML (lorsque condition de charge homogène). En fait, votre système fonctionnera beaucoup mieux avec 4 acteurs car il n'y aura que des frais généraux minimes en termes de planification et de gestion de la mémoire. Soit dit en passant, si votre système ne comporte que quelques acteurs, vérifiez votre pool de threads, qui essaie probablement de réutiliser le même thread pour le même acteur.

En général, le système ne fonctionnera pas plus rapidement si vous créez beaucoup d'acteurs.

Acteurs apatrides


Les acteurs sont correctement orientés objet (contrairement, disons, aux objets en Java): leur état n'est pas visible de l'extérieur, et ils communiquent par messages. Il est impossible de briser l'encapsulation, car vous ne pouvez pas regarder l'état de l'acteur pendant son travail. C'est tout l'intérêt des acteurs: ils vous donnent l'illusion d'un espace sûr dans lequel les messages sont exécutés séquentiellement, l'un après l'autre, vous permettant d'utiliser un état mutable à l'intérieur de votre acteur sans vous soucier des conditions de course (conditions de course - environ Per.) (Eh bien, presque sans vous inquiéter: l'essentiel est de ne pas laisser fuir l'état).

C'est pourquoi l'utilisation d'acteurs qui n'ont pas d'État est pour le moins étrange. À l'exception des acteurs qui contrôlent la surveillance de grandes parties de la hiérarchie du système d'acteurs (par exemple, la mise en place de superviseurs de sauvegarde), les acteurs sont vraiment conçus pour travailler avec de longs calculs qui ont un état. Parlant long, je veux dire que l'acteur traitera plusieurs messages tout au long de sa vie, produisant des résultats différents en fonction de son état, par opposition à des calculs ponctuels. Pour eux, les futurs sont une excellente abstraction: ils permettent l'exécution de code asynchrone, idéal pour les cas de travail avec un réseau ou une informatique liée au disque (ou des tâches de processeur vraiment intenses), peuvent être composées et avoir un mécanisme de traitement des défaillances. Ils s'intègrent également bien avec les acteurs Akka (en utilisant le modèle de pipe).

En général: n'utilisez pas d'acteurs si vous n'avez pas d'État - ils ne sont pas destinés à cela.

Source: https://habr.com/ru/post/fr420639/


All Articles