在Akka中,如何管理阻塞方法调用,例如从StdIn读取?

时间:2017-10-11 13:44:15

标签: scala akka actor

我开始使用Scala和AKKA模式,我已经编写了这段代码,但它不起作用,我不知道为什么......

我创建了一个从控制台读取用户输入的小项目。 当这个用户写了一个'关键字'时,keyWord Actor(Child)会解释它并与控制台Actor(Grand parent)进行通信。

动作演员将用于广播和做更多的事情。

当我在控制台Actor中输入命令'rename'时,我进入动作Actor,然后在keyWord Actor中输入并重命名方法,但之后没有,我没有进入重命名方法在控制台上的演员。

你能帮助我吗?

如果你看到任何错误的实践,请不要告诉我如何解决这个问题:)。

谢谢!

主要

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")
      }
    }
  }
}

keyWord actor

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 个答案:

答案 0 :(得分:2)

您不得阻止使用receive方法。您编写它的方式(使用while循环),初始ReadConsoleInput消息永远不会完成处理,并且任何后续消息(如StopLoop)将永远不会在Actor邮箱中保持不变。 / p>

如果您必须有选择地阅读StdIn(而不是继续阅读例如您的Main课程),那么一种方法可能是更改您的ConsoleActor,以便在收到时ReadConsoleInput StdIn.readLine消息,只需尝试ActionActor 一次,然后将结果转发给StdIn.readLine。由于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 => ... } 调用本身也是阻塞的,因此您应该异步执行。 " pipe"模式在这里派上用场:

ConsoleActor

这样,ActionActor立即再次可用于处理新邮件,而只要用户在控制台中输入一行,TreatInputUser就会收到ActionActor消息。

您可以在Await内应用相同的模式,而不是依赖StdIn.readLine

如果您想关闭循环以便继续发送消息,我可以使用behaviour确保两个Highcharts.chart('container', { title: { text: 'Linked Series'}, series: [{ id: 2, data: [3, 1, 3, 2] }, { id: 0, data: [2, 3, 2, 1] }, { id: 1, linkedTo: 2, data: [1, 2, 1, 3] }, ] }); 来电不会干扰。