Разница между Actorref.tell и inbox.send в Akka

Итак, я начал изучать Akka и пробовать примеры в typesafe. Итак, приложение Hello Akka имеет следующий код:

    import akka.actor.{ ActorRef, ActorSystem, Props, Actor, Inbox }
import scala.concurrent.duration._

case object Greet
case class WhoToGreet(who: String)
case class Greeting(message: String)

class Greeter extends Actor {
  var greeting = ""

  def receive = {
    case WhoToGreet(who) => greeting = s"hello, $who"
    case Greet           => sender ! Greeting(greeting) // Send the current greeting back to the sender
  }
}

object HelloAkkaScala extends App {

  // Create the 'helloakka' actor system
  val system = ActorSystem("helloakka")

  // Create the 'greeter' actor
  val greeter = system.actorOf(Props[Greeter], "greeter")

  // Create an "actor-in-a-box"
  val inbox = Inbox.create(system)

  // Tell the 'greeter' to change its 'greeting' message
  greeter.tell(WhoToGreet("akka"), ActorRef.noSender)

  // Ask the 'greeter for the latest 'greeting'
  // Reply should go to the "actor-in-a-box"
  inbox.send(greeter, Greet)

  // Wait 5 seconds for the reply with the 'greeting' message
  val Greeting(message1) = inbox.receive(5.seconds)
  println(s"Greeting: $message1")

  // Change the greeting and ask for it again
  greeter.tell(WhoToGreet("typesafe"), ActorRef.noSender)
  inbox.send(greeter, Greet)
  val Greeting(message2) = inbox.receive(5.seconds)
  println(s"Greeting: $message2")

  val greetPrinter = system.actorOf(Props[GreetPrinter])
  // after zero seconds, send a Greet message every second to the greeter with a sender of the greetPrinter
  system.scheduler.schedule(0.seconds, 1.second, greeter, Greet)(system.dispatcher, greetPrinter)

}

// prints a greeting
class GreetPrinter extends Actor {
  def receive = {
    case Greeting(message) => println(message)
  }
}

Теперь я застрял в понимании, в чем разница между ,

greeter.tell(WhoToGreet("akka"), ActorRef.noSender)

и

inbox.send(greeter, Greet)

Насколько я понимаю, актер начинает свою собственную ветку в конце

val inbox = Inbox.create(system)

Может кто-нибудь объяснить, что именно делает Actorref.tell, а затем что именно достигается строкой Inbox.send.


person Som Bhattacharyya    schedule 09.12.2014    source источник


Ответы (2)


Назначение tell, также представленного как !, состоит в том, чтобы отправить сообщение актеру. Поскольку акторы общаются посредством передачи сообщений, tell — это механизм, используемый для поддержки этой передачи сообщений. Это асинхронно для вызывающего, поэтому, как только вызывающий вызывает tell, он не связан с получением и обработкой этого сообщения экземпляром целевого субъекта. В этом конкретном примере код использует tell, чтобы актор приветствия обновлял свое внутреннее состояние. Поскольку это взаимодействие не приводит к какому-либо ответу (актер-получатель не отправляет сообщение обратно отправителю в ответ на этот запрос), здесь достаточно только tell.

Для получения приветственного ответа от приветствующего взаимодействие немного отличается. В этом взаимодействии отправитель ожидает ответного сообщения. Этот тип взаимодействия запрос/ответ может быть передан через ask (т. е. ?), в котором вызывающая сторона получает ответ Future, который будет завершен, когда получатель ответит, но ask здесь не используется. В этом примере кодировщик получил семантику запроса/ответа, используя вместо этого Inbox, который может вести себя как действующее лицо и устраняет необходимость в фьючерсах. Входящие должны выглядеть как ActorRef для получателя, что позволяет ему направить ответ обратно к нему со следующей строкой:

sender ! Greeting(greeting)

Метод sender() возвращает ActorRef того, кто отправил сообщение, которое в данный момент обрабатывается. Чтобы это работало, Inbox должен представлять себя как ActorRef. Но, как я уже говорил ранее, вы также могли бы использовать здесь ask, чтобы заставить работать взаимодействие запрос/ответ. Я думаю, это просто вопрос выбора. Суть в том, что tell используется для модели взаимодействия «выстрелил-забыл», а почтовый ящик (или другое действующее лицо или ask) можно использовать, когда требуется семантика запрос/ответ.

person cmbaxter    schedule 09.12.2014
comment
Значит ли это, что если я закомментирую вызовы inbox.send(greeter, Greet), я все равно увижу, что метод получения вызывается в классе актера Greeter? - person Som Bhattacharyya; 10.12.2014
comment
@SomBhattacharyya, в этом примере частичная функция receive будет вызываться два раза; один из tell из WhoToGreet и один из inbox.send из Greet сообщения. Если вы закомментируете inbox.send, то receive будет вызываться только один раз для сообщения WhoToGreet. - person cmbaxter; 10.12.2014

Когда вы делаете следующее, вы имеете дело непосредственно с ActorRef

greeter.tell(WhoToGreet("akka"), ActorRef.noSender)

С другой стороны, когда вы используете Inbox, вы имеете дело не с актером, а с подобным актеру. Один из сценариев, в котором это полезно, — это когда вы не хотите создавать своего собственного актера, но хотите асинхронно взаимодействовать с другими актерами. См. это

Входящие — это актор-подобный объект, который опрашивается извне. Он содержит актера, ссылка на который может быть передана другим актерам, как обычно, и он может наблюдать за жизненным циклом других актеров.

Актер работает в пуле потоков (настраивается с помощью диспетчера). Вы можете решить либо конфигурацией, либо вставив свой код, где какой диспетчер использует этот актор для своего выполнения.

person Soumya Simanta    schedule 09.12.2014