l'écriture d'applications avec les acteurs Scala dans la pratique II [clos]

StackOverflow https://stackoverflow.com/questions/1310964

  •  19-09-2019
  •  | 
  •  

Question

Parce que ma première question était si longtemps, je demande cela comme une question distincte. Il est un autre sur l'architecture d'une application basée sur l'acteur.

Garder la trace des chemins de messages à travers une application

Prenons un morceau de code Java:

public void deleteTrades(User user, Date date) {
    PermissionSet ps = permissionService.findPermissions(user)
    if (ps.hasPermission("delete")) {
        Set<Trade> ts = peristence.findTrades(date);
        reportService.sendCancelReports(ts);
        positionService.updateWithDeletedTrades(ts);
    }
}

Dans ce code, j'ai 4 composants séparés et l'interaction entre les nécessaires à la procédure deleteTrades bien définie. Il est entièrement contenu dans la méthode deleteTrades.

La modélisation de ce avec Actors et remplacer mes 4 composants avec 4 acteurs distincts, comment puis-je garder une trace (dans mon esprit) de quelle procédure implique? En particulier, si j'évitant d'utiliser le !? opérateur, alors il est probable que je vais envoyer un message à mon ConditionalDelete PermissionActor, qui sera d'envoyer un message à mon GetTradesAndDelete PersistenceActor qui sera ensuite envoyer d'autres messages etc etc Le code pour traiter une suppression sera semée dans mon application.

Cela signifie aussi que à peu près tous les acteurs a besoin d'une poignée sur tous les acteurs (afin de transmettre des messages).

Comme dans ma question précédente, comment les gens traitent avec cela? Y at-il un bon outil de modélisation qui vous permet de garder une trace de tout cela? Est-ce que les gens utilisent !? Suis-je tourner trop de composants dans Actors?

Était-ce utile?

La solution

Vous utilisez 5 composants, sans aucun doute. Il y a des acteurs qui traitent des tâches spécifiques, et il y a un orchestrateur aussi bien.

La question que vous devez avoir, bien sûr, est de savoir comment enchaînez-vous à ce assynchronously. Eh bien, il est en fait assez facile, mais il peut obscurcir le code. Fondamentalement, vous envoyez chaque componenent la réponse que vous voulez.

react {
  case DeleteTrades(user,dates) => 
    PermissionService ! FindPermissions(user, DeleteTradesPermissions(dates) _)
  case DeleteTradesPermissions(dates)(ps) =>
    if (ps hasPermission "delete")
      Persistence ! FindTrades(date, DeleteTradesTradeSet _)
  case DeleteTradesTradeSet(ts) =>
    ReportService ! SendCancelReports(ts)
    PositionService ! UpdateWithDeletedTrades(ts)
}

Ici, nous utilisons taitement passer « dates » dans la première réponse de retour. S'il y a beaucoup de paramètres associés à une interaction, il pourrait être préférable de garder l'information pour toutes les transactions en cours dans un HashSet local, et juste passer un jeton que vous utiliserez pour localiser ces informations lors de la réception de la réponse.

Notez que cet acteur unique peut gérer plusieurs actions simultanées. Dans ce cas particulier, il suffit de supprimer les transactions, mais vous pouvez ajouter un nombre illimité d'actions différentes pour qu'il puisse traiter. Lorsque les données nécessaires à une action est prêt, alors que l'action se poursuit.

EDIT

Voici un exemple concret de la façon dont on peut définir ces classes:

class Date
class User
class PermissionSet

abstract class Message
case class DeleteTradesPermission(date: Date)(ps: PermissionSet) extends Message
case class FindPermissions(u: User, r: (PermissionSet) => Message) extends Message

FindPermissions(new User, DeleteTradesPermission(new Date) _)

Quelques explications sur taitement et fonctions. La DeleteTradesPermission de classe est pour que vous étrillé pouvez passer un Date là-dessus, et ont une autre fonction complète avec un PermissionSet. Ce serait le modèle des messages de réponse.

Maintenant, la FindPermissions de classe reçoit un second paramètre fonction. L'acteur recevant ce message passera la valeur de retour à cette fonction, et recevra un Message à envoyer en réponse. Dans cet exemple, le message aura à la fois le Date, que l'acteur d'appel envoyé et PermissionSet, que l'acteur répondeur fournit.

Si aucune réponse est attendue, comme le cas de DeleteTrades, SendCancelReports et UpdateWithDeletedTrades aux fins de cet exemple, vous n'avez pas besoin de passer une fonction du message de retour.

Étant donné que nous nous attendons à une fonction qui renvoie un message en tant que paramètre pour les messages nécessitant une réponse, on pourrait définir des traits comme ceci:

trait MessageResponse1[-T1] extends Function1[T1, Message]
trait MessageResponse2[-T1, -T2] extends Function2[T1, T2, Message]
...

Autres conseils

Les acteurs ne doivent pas être utilisés pour remplacer les composants traditionnels de services sans considérations.

La plupart des composants de service que nous écrivons aujourd'hui, par la formation, sont apatrides. Apatrides composants de service sont plus faciles à gérer (sans classe de message, etc.) que les acteurs. L'une des choses qu'ils manquent cependant, quand comparer aux acteurs, est l'exécution asynchrone. Mais lorsque les clients attendent les résultats de revenir de façon synchrone la plupart du temps, les composants de service sans état synchrones sont très bien pour le travail.

Acteur est un bon ajustement quand il y a des états internes à gérer. Il n'y a pas besoin de faire une synchronisation à l'intérieur « acte () » pour accéder à des états internes et à se soucier des conditions de course. Aussi longtemps que "!?" n'est pas utilisé à l'intérieur « acte () », interblocage devrait être réduite au minimum aussi bien.

Les acteurs doivent se méfier de tout traitement de blocage fait alors que les messages de manipulation. Étant donné que les acteurs traitent leurs messages de manière séquentielle, chaque fois qu'ils sont bloqués en attente d'E / S à l'intérieur « acte () », ils ne peuvent pas traiter d'autres messages dans leur boîte aux lettres. L'idiome Scala à utiliser ici est de commencer un autre acteur ad hoc qui fait l'opération de blocage réelle. Cette anti-modèle affecte l'acteur à base d'événements (réagir) encore plus car il bloque le fil de l'acteur événementiel est sauvegardé sur piggy ainsi.

D'après ce que je peux rassembler, tous les quatre de vos appels vers les composants de service sont potentiellement blocage, si soins doivent être prises pour les rendre échelle lors de leur conversion aux acteurs.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top