2017-10-11 3 views
1

Je commence à utiliser Scala et le modèle AKKA, et j'ai écrit ce code, mais ça ne marche pas et je ne sais pas pourquoi ...Dans Akka, comment gérer les appels de méthode de blocage, comme lire depuis StdIn?

J'ai créé un petit projet qui lit l'utilisateur entrée de la console. lorsque cet utilisateur a écrit un mot-clé, l'acteur keyword (enfant) l'interprétera et communiquera avec l'acteur de la console (grand parent).

l'action Acteur sera utilisé pour diffuser et faire plus de choses. Quand j'entre la commande 'renommer' dans la console Acteur, j'entre dans l'action Acteur et ensuite dans le mot clé Acteur et j'entre dans la méthode Renommer, mais après rien, je ne rentre pas dans le renommer

méthode sur la console Acteur.

Pouvez-vous m'aider?

Si vous avez vu une mauvaise pratique, n'hésitez pas à me dire comment résoudre cela :).

Merci!

principal

import ConsoleActor._ 
import akka.actor.ActorSystem 

object Main extends App { 
    val system = ActorSystem("My-Little-IRC") 

    val consoleActor = system.actorOf(ConsoleActor.props, "consoleActor") 

    consoleActor ! ReadConsoleInput 
    system.terminate() 
} 

consoleActor

import ActionActor.TreatInputUser 
import akka.actor.{Actor, Props} 

import scala.io.StdIn 

object ConsoleActor { 

    case class ReadConsoleInput() 
    case class StopLoop() 
    case class Rename() 
    case class WhoIAm() 
    def props = Props[ConsoleActor] 
} 

class ConsoleActor() extends Actor { 
    val keyWordActor = context.actorOf(KeyWordActor.props(this.self), "keyWordActor") 

    val actionActor = context.actorOf(ActionActor.props(keyWordActor), "actionActor") 

    var currentUser: String = "" 
    var loop: Boolean = true; 

    import ConsoleActor._ 

    def isValidString(str: String): Boolean = { 
    var isValid: Boolean = false 

    if (str != null && !str.trim().isEmpty) 
     isValid = true 

    isValid 
    } 

    def initiatePresentation() = { 
    println("Hi ! Who are you ?") 
    currentUser = StdIn.readLine() 
    println(s"Nice to meet you ${currentUser}, I'm your console app !") 

    } 

    def receive = { 
    case ReadConsoleInput => { 
     initiatePresentation 
     var value = "" 
     while (loop) { 
     println("Yes ?") 
     value = StdIn.readLine() 
     if (isValidString(value)) { 
      actionActor ! TreatInputUser(value) 

     } 
     } 
    } 

    case StopLoop => { 
     println("stop Loooop !") 
     loop = false 
    } 
    case Rename => { 
     println(s"${currentUser} was a good name ! Which is your new name ?") 
     currentUser = StdIn.readLine() 
     println(s"Nice to meet you -again- ${currentUser}") 
    } 
    case WhoIAm =>{ 
     println(s"I'm ${currentUser}") 
    } 
    } 
} 

actionActor

import ActionActor.TreatInputUser 

import akka.actor.{Actor, ActorRef, Props} 
import akka.util.Timeout 
import scala.concurrent.duration._ 
import akka.pattern.ask 

import scala.concurrent.Await 


object ActionActor { 
    case class TreatInputUser(string: String) 
    def props(keyWordActor: ActorRef) = Props(new ActionActor(keyWordActor)) 
} 

class ActionActor(keyWordActor: ActorRef) extends Actor { 
    import KeyWordActor._ 

    def receive = { 
    case TreatInputUser(string) => { 
     implicit val timeout = Timeout(5 seconds) 
     var isKeyWord = keyWordActor ? IsKeyWord(string) 
     val isKeyWordResult = Await.result(isKeyWord, timeout.duration).asInstanceOf[ Boolean ] 
     println(isKeyWordResult) 
     if (isKeyWordResult) { 
     keyWordActor ! HandleKeyWord(string) 
     } 
     else { 
     println("bla bla bla") 
     } 
    } 
    } 
} 

acteur de mot-clé

import ConsoleActor.{Rename, StopLoop, WhoIAm} 
import akka.actor.{Actor, ActorRef, Props} 

object KeyWordActor { 
    case class IsKeyWord(key : String) 
    case class HandleKeyWord(key : String) 

    def props(consoleActor: ActorRef) = Props(new KeyWordActor(consoleActor)) 
} 


class KeyWordActor(consoleActor: ActorRef) extends Actor { 

    import KeyWordActor._ 

    val KeysWord : Map[ String,()=> Any] = Map("rename" -> renameFunction, "stop" -> stopFunction, "42" -> uselessfunction, "john doe ?" -> AmIJohnDoe) 

    def renameFunction() = { 
    println("here") 
    consoleActor ! Rename 
    } 

    def stopFunction() = { 
    consoleActor ! StopLoop 
    } 

    def uselessfunction() = { 
    println("useless") 
    } 

    def AmIJohnDoe() ={ 
    consoleActor ! WhoIAm 
    } 

    def receive = { 
    case IsKeyWord(key) => { 
     sender ! KeysWord.contains(key.toLowerCase) 
    } 
    case HandleKeyWord(key) => { 
     if (KeysWord.contains(key.toLowerCase)) { 
     KeysWord(key.toLowerCase)() 
     } 
    } 
    } 
} 
+1

Dans 'ConsoleActor', vous bloquez dans votre méthode' receive'.[Le blocage nécessite une gestion attentive] (https://doc.akka.io/docs/akka/2.5/scala/dispatchers.html#blocking-needs-careful-management). Plutôt, lisez à partir de 'StdIn' dans' Main', et envoyez un message à 'ConsoleActor' (ou quelqu'un de approprié) quand une ligne est lue. –

+0

Hey @ LászlóvandenHoek, quand vous avez dit que je devais lire l'entrée de l'utilisateur dans le'Main ', voulez-vous dire que la boucle' While 'doit être dans le' Main '? Si oui, comment l'acteur 'keyWord' pourra-t-il arrêter la boucle? – Brice

+0

Mon commentaire précédent est, mais maintenant que j'ai bien compris votre question, j'ai fourni une réponse. –

Répondre

2

Vous ne devez pas bloquer dans la méthode receive. De la façon dont vous l'avez écrit (avec une boucle while), le message ReadConsoleInput initial ne termine jamais le traitement et tous les messages suivants (tels que StopLoop) resteront intacts dans la boîte aux lettres Actor pour toujours.

Si vous devez sélectivement lire StdIn (par opposition à la simple lecture en continu par exemple, dans votre Main classe), puis une approche pourrait être de changer ConsoleActor de telle sorte que lorsqu'il reçoit un message ReadConsoleInput, il devrait juste essayer de faire StdIn.readLineune fois, et transmettre le résultat au ActionActor. Puisque l'appel StdIn.readLine lui-même bloque également, vous devriez le faire de manière asynchrone. Le modèle « pipe » est ici à portée de main:

import akka.pattern.pipe 
import scala.concurrent.Future 

//... 

def receive = { 
    case ReadConsoleInput => 
    import context.dispatcher //provide a thread pool to do async work 
    Future(StdIn.readLine()) //read a line of input asynchronously 
     .filter(isValidString) //only continue if line is valid 
     .map(TreatInputUser) //wrap the (valid) String in a message 
     .pipeTo(actionActor) //forward it 
    case Rename => ... 
} 

De cette façon, le ConsoleActor devient à nouveau disponible immédiatement pour traiter les nouveaux messages, alors que votre ActionActor recevra un message TreatInputUser chaque fois que l'utilisateur a terminé de taper une ligne dans la console.

Vous pouvez appliquer le même modèle à l'intérieur de votre ActionActor, au lieu de dépendre de Await.

Si vous souhaitez fermer la boucle afin de pouvoir continuer à envoyer des messages, j'utiliserais behaviour pour m'assurer que deux appels StdIn.readLine n'interfèrent pas.

+0

Maintenant, je peux atteindre la «console Acteur» si le «acteur KeyWord» Merci! mais maintenant, je peux seulement écrire une commande, j'ai perdu la possibilité d'écrire plus d'un. Qu'est-ce que je devrais changer pour pouvoir écrire plus d'une commande? – Brice