В прошлый раз, когда мы увидели обмен сообщениями между актером, мы увидели, как отправляются сообщения «забей-и-забудь» (это означает, что мы просто отправляем сообщение актеру, но не ожидаем ответа от актера).
Технически, мы посылаем сообщения Актерам за их побочные эффекты ВСЕ ВРЕМЯ. Это по замыслу. Помимо не отвечая, целевой актер также может сделать следующее сообщение:
-
Отправьте ответ отправителю (в нашем случае
TeacherActorон ответит цитатой обратно вStudentActorИЛИ -
Направьте ответ обратно другому актеру, который может быть целевой аудиторией, который, в свою очередь, может ответить / переслать / иметь побочный эффект. Маршрутизаторы и супервизоры являются примерами таких случаев. (мы посмотрим на них очень скоро)
ОТВЕТ НА ЗАПРОС
В этой статье мы сосредоточимся только на пункте 1 — цикле запрос-ответ.
Картина передает то, что мы пытаемся достичь в этот раз. Для краткости я не представлял ActorSystem, Dispatcher или Mailboxes на рисунке.
-
DriverAppПосылаетInitSignalсообщение вStudentActor. -
В
StudentActorреагирует наInitSignalсообщения и отправляетQuoteRequestсообщение вTeacherActor. -
Ответ
TeacherActor, как мы видели в первом обсуждении , сQuoteResponse. -
StudentActorПросто регистрируетQuoteResponseв консоли / регистратора.
Мы также подготовим контрольный пример, чтобы проверить это.
Давайте теперь посмотрим на эти 4 пункта подробнее:
1. DRIVERAPP ОТПРАВЛЯЕТ INITSIGNAL СООБЩЕНИЕ В STUDENTACTOR
К настоящему времени вы бы уже догадались, что будет DriverApp делать. Всего 4 вещи:
1) Инициализировать ActorSystem
//Initialize the ActorSystem
val system = ActorSystem("UniversityMessageSystem")
2) Создать TeacherActor
//create the teacher actor val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor")
3) Создать StudentActor
//create the Student Actor - pass the teacher actorref as a constructor parameter to StudentActor val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor")
Вы заметите , что я передаю в ActorRef из TeacherActor к конструктору , StudentActor так что StudentActor может использовать ActorRef для отправки сообщений в TeacherActor. Есть и другие способы достижения этого (например, передача в Props ), но этот метод пригодится, когда мы рассмотрим Супервизоры и Маршрутизаторы в следующих статьях. Мы также довольно скоро рассмотрим детей-актеров, но это не будет семантически правильным подходом — учитель, создающий Учителя, звучит не очень хорошо. Является ли?
И, наконец,
4) DriverApp Затем объект отправляет сообщение InitSignal в StudentActor, чтобы StudentActor мог начать отправку сообщения QuoteRequest в TeacherActor.
//send a message to the Student Actor studentRef ! InitSignal
Это в значительной степени DriverClass. Thread.sleep И ActorSystem.shutdown просто ждать в течение нескольких секунд для отправки сообщения , чтобы закончить , прежде чем мы , наконец , закрыли ActorSystem.
DRIVERAPP.SCALA
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 СООБЩЕНИЕ НА TEACHERACTOR
И
4. STUDENTACTOR ПОЛУЧАЕТ QUOTERESPONSE ОТ TEACHERACTOR И ПРОСТО ЖУРНАЛЫ НА КОНСОЛЬ / РЕГИСТРАТОР
Почему я соединил пункты 2 и 4? Потому что это так просто, что ты меня возненавидишь, если я их разлучу.
Итак, точка 2 — StudentActor получает InitSignal сообщение от DriverApp и отправляет QuoteRequest TeacherActor.
def receive = {
case InitSignal=> {
teacherActorRef!QuoteRequest
}
...
...
Это оно !!!
Пункт 4 — StudentActor регистрирует сообщение, полученное от TeacherActor.
Как и обещал
case QuoteResponse(quoteString) => {
log.info ("Received QuoteResponse from Teacher")
log.info(s"Printing from Student Actor $quoteString")
}
Я уверен, что вы согласитесь, что теперь это выглядит почти как псевдокод.
Итак, весь StudentActor класс выглядит так:
STUDENTACTOR.SCALA
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.
Это тот же самый код, который мы видели в рецензии на запуск .
TeacherActor получает QuoteRequest сообщение и отправляет QuoteResponse обратно.
TEACHERACTOR.SCALA
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. Так как StudentActor просто регистрирует сообщение, и мы не сможем утверждать на самом QuoteResponse, мы просто подтвердим наличие сообщения журнала в EventStream (как мы говорили в прошлый раз )
Итак, наш тестовый пример выглядит так:
"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.



