Статьи

Акка Заметки — Актер Обмен сообщениями — Запрос и ответ — 3

В прошлый раз, когда мы увидели обмен сообщениями с актером, мы увидели, как отправляются сообщения «забей-и-забудь» (то есть мы просто отправляем сообщение актеру, но не ожидаем ответа от актера).

Технически, мы посылаем сообщения Актерам за их побочные эффекты ВСЕ ВРЕМЯ. Это по замыслу. Помимо не отвечая, целевой актер также может сделать следующее сообщение:

  1. Отправьте ответ отправителю (в нашем случае TeacherActor ответит цитатой обратно StudentActor ИЛИ
  2. Направьте ответ обратно другому актеру, который может быть целевой аудиторией, который, в свою очередь, может ответить / переслать / иметь побочный эффект. Маршрутизаторы и супервизоры являются примерами таких случаев (мы рассмотрим их очень скоро).

Ответ на запрос

В этой статье мы сосредоточимся только на пункте 1 — цикле запрос-ответ.

полный

Картина передает то, что мы пытаемся достичь в этот раз. Для краткости я не представлял ActorSystem, Dispatcher или Mailboxes на рисунке.

  1. DriverApp отправляет сообщение StudentActor в StudentActor .
  2. StudentActor реагирует на сообщение InitSignal и отправляет сообщение QuoteRequest в QuoteRequest .
  3. TeacherActor , как мы видели в первом обсуждении , отвечает QuoteResponse .
  4. StudentActor просто регистрирует QuoteResponse для консоли / регистратора.

Мы также подготовим контрольный пример, чтобы проверить это.

Давайте теперь посмотрим на эти 4 пункта подробнее:

1. DriverApp отправляет сообщение StudentActor в StudentActor

DriverApp

К настоящему времени вы бы догадались, что бы DriverApp . Всего 4 вещи:

  1. Инициализировать ActorSystem
  2. 1
    2
    //Initialize the ActorSystem
      val system = ActorSystem("UniversityMessageSystem")
  3. Создать TeacherActor
  4. 1
    2
    //create the teacher actor
      val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor")
  5. Создать StudentActor
  6. 1
    2
    //create the Student Actor - pass the teacher actorref as a constructor parameter to StudentActor
      val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor")

    Вы заметите, что я передаю ActorRef объекта ActorRef конструктору StudentActor чтобы StudentActor мог использовать ActorRef для отправки сообщений в TeacherActor. Есть и другие способы достижения этого (например, передача в Props ), но этот метод пригодится, когда мы рассмотрим Супервизоры и Маршрутизаторы в следующих статьях. Мы также довольно скоро рассмотрим детей-актеров, но это не будет семантически правильным подходом — учитель, создающий Учителя, не звучит хорошо Является ли?

    И, наконец,

  7. Затем DriverApp отправит InitSignal в StudentActor , чтобы StudentActor мог начать отправку сообщения QuoteRequest в TeacherActor.
1
2
//send a message to the Student Actor
  studentRef ! InitSignal

Это в значительной степени DriverClass. Thread.sleep и ActorSystem.shutdown просто ждут пару секунд, пока не закончится отправка сообщения, прежде чем мы наконец выключим ActorSystem.

DriverApp.scala

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package me.rerun.akkanotes.messaging.requestresponse
 
import akka.actor.ActorSystem 
import akka.actor.Props 
import me.rerun.akkanotes.messaging.protocols.StudentProtocol._ 
import akka.actor.ActorRef
 
object DriverApp extends App {
 
  //Initialize the ActorSystem
  val system = ActorSystem("UniversityMessageSystem")
 
  //construct the teacher actor
  val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor")
 
  //construct the Student Actor - pass the teacher actorref as a constructor parameter to StudentActor
  val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor")
 
  //send a message to the Student Actor
  studentRef ! InitSignal
 
  //Let's wait for a couple of seconds before we shut down the system
  Thread.sleep(2000)
 
  //Shut down the ActorSystem.
  system.shutdown()
 
}

2. StudentActor реагирует на сообщение InitSignal и отправляет сообщение QuoteRequest в QuoteRequest

И

4. StudentActor получает QuoteResponse от QuoteResponse и просто входит в консоль / регистратор.

Почему я соединил пункты 2 и 4? Потому что это так просто, что ты меня возненавидишь, если я их разлучу.

StudentTeacher

Итак, пункт 2 — StudentActor получает сообщение InitSignal от DriverApp и отправляет QuoteRequest в QuoteRequest .

1
2
3
4
5
6
def receive = { 
    case InitSignal=> {
          teacherActorRef!QuoteRequest
    }
    ...
    ...

Это оно !!!

Пункт 4 — StudentActor регистрирует сообщение, полученное от TeacherActor.

Ученик

Как и обещал

1
2
3
4
case QuoteResponse(quoteString) => { 
      log.info ("Received QuoteResponse from Teacher")
      log.info(s"Printing from Student Actor $quoteString")
}

Я уверен, что вы согласитесь, что теперь это выглядит почти как псевдокод.

Итак, весь класс StudentActor выглядит так:

StudentActor.scala

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package me.rerun.akkanotes.messaging.requestresponse
 
import akka.actor.Actor 
import akka.actor.ActorLogging 
import me.rerun.akkanotes.messaging.protocols.TeacherProtocol._ 
import me.rerun.akkanotes.messaging.protocols.StudentProtocol._ 
import akka.actor.Props 
import akka.actor.ActorRef
 
class StudentActor (teacherActorRef:ActorRef) extends Actor with ActorLogging {
 
  def receive = {
    case InitSignal=> {
      teacherActorRef!QuoteRequest
    }
 
    case QuoteResponse(quoteString) => {
      log.info ("Received QuoteResponse from Teacher")
      log.info(s"Printing from Student Actor $quoteString")
    }
  }
}

3. TeacherActor отвечает QuoteResponse .

Это тот же самый код, который мы видели в переписке с помощью fire-n-забудьте .

TeacherActor получает сообщение QuoteRequest и отправляет QuoteResponse обратно.

TeacherActor.scala

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package me.rerun.akkanotes.messaging.requestresponse
 
import scala.util.Random
 
import akka.actor.Actor 
import akka.actor.ActorLogging 
import akka.actor.actorRef2Scala 
import me.rerun.akkanotes.messaging.protocols.TeacherProtocol._
 
 
class TeacherActor extends Actor with ActorLogging {
 
  val quotes = List(
    "Moderation is for cowards",
    "Anything worth doing is worth overdoing",
    "The trouble is you think you have time",
    "You never gonna know if you never even try")
 
  def receive = {
 
    case QuoteRequest => {
 
      import util.Random
 
      //Get a random Quote from the list and construct a response
      val quoteResponse = QuoteResponse(quotes(Random.nextInt(quotes.size)))
 
      //respond back to the Student who is the original sender of QuoteRequest
      sender ! quoteResponse
 
    }
  }
}

Testcases

Теперь наш DriverApp будет имитировать DriverApp . Так как StudentActor просто регистрирует сообщение, и мы не сможем утверждать на самом QuoteResponse, мы просто подтвердим наличие сообщения журнала в EventStream (как мы говорили в прошлый раз ).

Итак, наш тестовый пример выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
"A student" must {
 
    "log a QuoteResponse eventually when an InitSignal is sent to it" in {
 
      import me.rerun.akkanotes.messaging.protocols.StudentProtocol._
 
      val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor")
      val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor")
 
      EventFilter.info (start="Printing from Student Actor", occurrences=1).intercept{
        studentRef!InitSignal
      }
    }
  }

Код

Весь проект можно скачать с github здесь .

Далее мы увидим, как использовать планировщики в Akka и контролировать ваше приложение Akka с помощью Kamon .

Ссылка: Примечания Akka — Сообщения актеров — Запрос и ответ — 3 от нашего партнера JCG Аруна Маниваннана в блоге Rerun.me .