Comment fonctionne E/S dans Akka?
Comment fonctionne le modèle d'acteur (dans Akka) lorsque vous devez effectuer des E/S (ie. une opération de base de données)?
Je crois comprendre qu'une opération de blocage lancera une exception (et ruinera essentiellement toute concurrence en raison de la nature évitée de Netty, qu'Akka utilise). Par conséquent, je devrais utiliser un Future
ou quelque chose de similaire-mais je ne comprends pas le modèle de concurrence.
- un acteur peut-il traiter plusieurs messages simultanément?
- si un acteur fait un appel de blocage dans un
future
(ie.future.get()
) cela bloque-t-il seulement l'exécution de l'acteur actuel; ou empêchera-t-il l'exécution sur tous les acteurs jusqu'à ce que l'appel de blocage soit terminé? - s'il bloque toute exécution, comment l'utilisation d'une concurrence future aide-t-elle la concurrence (ie. l'appel d'appels de blocage dans un futur ne reviendrait-il pas à créer un acteur et à exécuter l'appel de blocage)?
- Quelle est la meilleure façon de traiter un processus multi-étapes (ie. lire à partir de la base de données; appeler un service web bloquant; lire de la base de données; écrire dans la base de données) où chaque étape dépend de la dernière?
Le contexte de base est le suivant:
- j'utilise un serveur Websocket qui maintiendra des milliers de sessions.
- chaque session a un certain état (ie. détails d'authentification, etc.);
- le client Javascript enverra un message JSON-RPC au serveur, qui le transmettra à l'acteur de session approprié, qui l'exécutera et retournera un résultat.
- exécution du RPC l'appel impliquera des E / S et des appels bloquants.
- il y aura un grand nombre de requêtes simultanées (chaque utilisateur fera une quantité importante de requêtes sur la connexion WebSocket et il y aura beaucoup d'utilisateurs).
Y a-t-il une meilleure façon d'y parvenir?
3 réponses
Les opérations de blocage ne déclenchent pas D'exceptions dans Akka. Vous Pouvez bloquer les appels d'un acteur (que vous voulez probablement minimiser, mais c'est une autre histoire).
- non, 1 instance d'acteur ne peut pas.
- il ne bloquera aucun autre acteur. Vous pouvez influencer cela en utilisant un répartiteur spécifique. Les Futures utilisent le répartiteur par défaut (l'événement global piloté normalement) afin qu'il s'exécute sur un thread dans un pool. Vous pouvez choisir le répartiteur que vous souhaitez utiliser pour vos acteurs (par l'acteur, ou pour tous). Je suppose que si vous vouliez vraiment créer un problème, vous pourriez être en mesure de passer exactement le même répartiteur (basé sur des threads) aux futures et aux acteurs, mais cela prendrait une certaine intention de votre part. Je suppose que si vous avez un grand nombre de contrats à terme bloquant indéfiniment et que l'executorservice a été configuré pour une quantité fixe de threads, vous pourriez faire sauter l'executorservice. Donc beaucoup de "si". A F. obtenir des blocs seulement si L'avenir n'est pas encore terminé. Il va bloquer le ' courant thread ' de L'acteur à partir duquel vous l'appelez (si vous l'appelez d'un acteur, ce qui n'est pas nécessaire en passant)
- vous n'avez pas nécessairement à bloc. vous pouvez utiliser un rappel au lieu de F. get. Vous pouvez même composer des contrats à terme sans bloquer. consultez le discours de Viktor sur 'l'avenir prometteur d'akka' pour plus de détails: http://skillsmatter.com/podcast/scala/talk-by-viktor-klang
- j'utiliserais une communication asynchrone entre les étapes (si les étapes sont des processus significatifs sur leur propre), donc utiliser un acteur pour chaque étape, où chaque acteur envoie un message oneway à l'autre, peut-être aussi des messages oneway à un autre acteur qui ne bloquera pas qui peut superviser le processus. De cette façon, vous pouvez créer des chaînes d'acteurs, dont vous pouvez faire beaucoup, en face de cela, vous pouvez mettre un acteur d'équilibrage de charge, de sorte que si un acteur bloque dans une chaîne, un autre du même type pourrait ne pas être dans l'autre chaîne. Cela fonctionnerait également pour votre question "contexte", passer de la charge de travail à acteurs locaux, enchaînez-les derrière un acteur d'équilibrage de charge.
Quant à netty (et je suppose que vous voulez dire des acteurs distants, parce que c'est la seule chose pour laquelle netty est utilisée dans Akka), passez votre travail dès que possible à un acteur local ou à un futur (avec callback) si vous êtes inquiet pour le timing ou empêcher netty de faire son travail d'une manière ou d'une autre.
Les opérations de blocage ne déclenchent généralement pas d'exceptions, mais l'attente d'un futur (par exemple en utilisant les méthodes !!
ou !!!
send) peut déclencher une exception de délai d'attente. C'est pourquoi vous devriez rester avec le feu et oublier autant que possible, utiliser une valeur de délai d'attente significative et préférer les rappels lorsque cela est possible.
Un acteur akka ne peut pas explicitement traiter plusieurs messages d'affilée, mais vous pouvez jouer avec la valeur
throughput
via le fichier de configuration. L'acteur sera ensuite procédé à la plusieurs messages (c'est-à-dire que sa méthode de réception sera appelée plusieurs fois séquentiellement) si sa file d'attente de messages n'est pas vide: http://akka.io/docs/akka/1.1.3/scala/dispatchers.html#id5Le blocage des opérations dans un acteur ne "bloque" pas tous les acteurs, mais si vous partagez des threads entre acteurs (Utilisation recommandée), l'un des threads du répartiteur sera bloqué jusqu'à ce que les opérations reprennent. Alors essayez de composer des contrats à terme autant que possible et méfiez-vous du temps mort valeur).
3 et 4. Je suis d'accord avec les réponses de Raymond.
Ce que Raymond et paradigmatic ont dit, mais aussi, si vous voulez éviter de mourir de faim dans le pool de threads, vous devriez envelopper toutes les opérations de blocage dans scala.concurrent.blocking
.
Il est bien sûr préférable d'éviter les opérations de blocage, mais parfois vous devez utiliser une bibliothèque qui bloque. Si vous enveloppez ce code dans blocking
, Il fera savoir au contexte d'exécution que vous bloquez peut-être ce thread afin qu'il puisse en allouer un autre si nécessaire.
Le problème est pire que paradigmatique décrit puisque si vous avez plusieurs opérations de blocage vous pouvez finir par Bloquer tous les threads du pool de threads et n'avoir aucun thread libre. Vous pourriez vous retrouver dans l'impasse si tous vos threads sont bloqués sur quelque chose qui n'arrivera pas jusqu'à ce qu'un autre acteur/futur soit programmé.
Voici un exemple:
import scala.concurrent.blocking ... Future { val image = blocking { load_image_from_potentially_slow_media() } val enhanced = image.enhance() blocking { if (oracle.queryBetter(image, enhanced)) { write_new_image(enhanced) } } enhanced }
La Documentation est ici.