2017-10-18 12 views
1

J'utilise Akka pour construire mon système de messagerie à l'exécution. Mais il souffre souvent de la situation embarrassante: de nombreux messages sont envoyés avec ! et il est difficile de tracer la logique métier. A partir du livre de Programming in Erlang, il n'est pas recommandé d'exposer un message à d'autres personnes mais d'encapsuler le message d'acteur avec une fonction et de l'exporter avec export([func1/1]) ou autre chose.comment éviter de passer le message brut partout quand utiliser Akka pour construire un programme?

Alors, est-il possible d'utiliser le modèle d'appel de fonction dans Akka?
Comment lisez-vous le code avec autant de messages lorsque vous utilisez Akka pour construire un grand système?

Répondre

1

Le nouveau support typé Akka expérimental va un long chemin à aider à comprendre et trace la logique métier:

https://doc.akka.io/docs/akka/2.5/scala/typed.html

Ce que cela fait est vous permet d'encoder les protocoles de parler aux acteurs du système de type, ce qui peut faciliter la compréhension du fonctionnement de votre système lorsque vous lisez le code.

Une autre option consiste à utiliser Cinnamon (une bibliothèque commerciale Lightbend) pour tracer des messages, ce qui vous permet de comprendre le flux de contrôle lors de l'exécution. Voici un blog à ce sujet:

https://developer.lightbend.com/blog/2017-05-08-cinnamon-2-4-with-opentracing-integration/

Si vous faites beaucoup de demande sur vos acteurs, un modèle commun est de mettre que derrière des méthodes qui renvoient à terme. Par exemple:

class MyService(actor: ActorRef) { 
    def doSomething(msg: Something): Future[Result] = { 
    (actor ? msg).mapTo[Result] 
    } 
} 

Bien sûr, vous pouvez appliquer la même chose à régulière dit, sauf que votre type de retour va être Unit.

Enfin, un modèle commun à Akka est de mettre toutes les classes de message dans l'objet compagnon de l'acteur:

object MyActor { 
    /** This message does something */ 
    case class MessageOne(foo: String) 
    /** This message does something else */ 
    case class MessageTwo(bar: String) 
} 
class MyActor extends Actor { 
    import MyActor._ 
    override def receive = { 
    case MessageOne(foo) => 
     ... 
    case MessageTwo(bar) => 
     ... 
    } 
} 

Alors que peut-être cela ne suffit pas de suivre la logique métier, elle ne le rendre plus facile pour quelqu'un de nouveau au système pour avoir une idée de la façon de parler à un acteur en mettant tous les messages (avec la documentation) en un seul endroit.

Aussi un mot sur la structuration de votre code - les acteurs sont censés avoir une responsabilité limitée avec un protocole bien défini. Un système basé sur Akka bien conçu n'exigera pas que vous compreniez le flux entier de messages partout à la fois, il devrait être simple de raisonner sur chaque acteur individuellement. L'utilisation attentive des hiérarchies d'acteurs pour extraire des processus enfants en tant qu'enfants enfants peut vraiment aider ici. En d'autres termes, si pour comprendre comment votre système se comporte, vous devez comprendre comment coulent des milliers de messages entre des centaines d'acteurs, alors vous avez certainement un problème de conception et devez repenser comment vous pouvez briser vos acteurs dans des unités isolées qui peuvent être facilement assemblées comme des blocs de construction.

+0

* Sûrement * il existe un meilleur système que celui-ci. Je veux dire, son Akka. Erlang existe depuis toujours et ... sérieusement? – zxq9

+0

Bon conseil! Un peu de réflexion sur mettre mesaage dans un objet n'est peut-être pas toujours une solution de contournement. Tels que, si j'ai un message de classe de cas vouloir utilisé à deux acteurs ou plus définit.Besides, je pense que l'acteur typé a sa limite parce que nous ne pouvons pas promettre que tout le message reçu est bien typé, il appartient à ce que fait l'acteur. – LoranceChen

0

Vous pourriez faire la même chose qu'Erlang et cacher tout le message derrière les fonctions. En d'autres termes, plutôt que de traiter avec des acteurs, vous devez traiter des classes générales (abstraction de domaine) et utiliser les acteurs uniquement comme sous-jacent à la mise en œuvre.

case class Cat(name: String) 


trait Shelter { 
    def leave(cat: Cat): Unit 
} 


class ActorShelter extends Shelter with Actor { 

    var cats = List[Cat]() 

    def leave(cat: Cat) = 
    this.self ! cat 

    def receive = { 
    case cat: Cat => 
     cats = cat :: cats 
    } 
} 

Cela vous permettra d'utiliser Shelter dans votre code sans jamais savoir qu'il est un acteur.

Bien sûr, vous devez toujours décider si vous allez retourner Unit sur tous les moulages avec !, ou peut-être juste référence à vous-même. Et si vous renverrez Future sur les appels avec ?, ou voulez-vous attendre la valeur réelle.

+0

droit mais efficace – LoranceChen

+1

Mais un problème est quand je crée un 'ActorShelter' que je reçois seulement un type' ActorRef'. Comment puis-je obtenir «Shelter»? – LoranceChen

+0

C'est un très bon point, et légèrement plus compliqué. Dans Erlang vous pouvez juste engendrer un nouveau processus/acteur, dans Akka vous devez le faire dans un certain contexte (système ou autre acteur déjà existant). Et cela peut varier en fonction de la façon dont vous structurez votre application, il n'y a donc pas de solution unique. Je vais essayer d'enquêter un peu plus et peut-être mettre à jour ma réponse. – mpm