上次看到Actor消息时,我们看到了发送fire-n-forget消息的方式(意思是,我们只是向Actor发送消息,但不希望Actor做出响应)。
从技术上讲,我们始终向其发送有关其副作用的消息。 这是设计使然。 除了不响应之外,目标Actor还可通过该消息执行以下操作:
- 将回复发送回发件人(在我们的示例中,
TeacherActor
会以引号回复给StudentActor
或 - 将响应转发回其他可能是目标受众的Actor,而后者又可能会做出响应/转发/有副作用。 路由器和管理程序就是这些情况的示例(我们将很快对其进行介绍)。
要求与回应
在本文中,我们将仅关注点1-请求-响应周期。
图片传达了我们这次要实现的目标。 为了简洁起见,我没有在图片中表示ActorSystem,Dispatcher或Mailboxes。
-
DriverApp
将InitSignal
消息发送给StudentActor
。 - 所述
StudentActor
反作用于InitSignal
消息并发送QuoteRequest
消息给TeacherActor
。 - 就像我们在第一次讨论中看到的那样,
TeacherActor
用QuoteResponse
进行响应。 -
StudentActor
只需将QuoteResponse
记录到控制台/记录器。
我们还将准备一个测试用例进行验证。
现在让我们详细看一下这4点:
1.
到现在DriverApp
,您可能已经猜出了DriverApp
做什么。 只需四件事:
- 初始化
ActorSystem
- 创建
TeacherActor
- 创建
StudentActor
- 该
DriverApp
然后将发送InitSignal
到StudentActor
,使StudentActor可以开始发送QuoteRequest消息给TeacherActor。
//Initialize the ActorSystem
val system = ActorSystem("UniversityMessageSystem")
//create the teacher actor
val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor")
//create the Student Actor - pass the teacher actorref as a constructor parameter to StudentActor
val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor")
您会注意到,我将TeacherActor
的ActorRef
传递给StudentActor
的构造函数,以便StudentActor可以使用ActorRef将消息发送给TeacherActor。 还有其他方法可以实现此目的(例如传递Props ),但是当我们在以下文章中介绍Supervisor和Routers时,此方法将派上用场。 我们也将很快着眼于儿童演员,但这在语义上不是正确的方法–学生创建老师听起来并不好。 可以?
最后,
//send a message to the Student Actor
studentRef ! InitSignal
差不多就是DriverClass。 在最终关闭ActorSystem之前, Thread.sleep
和ActorSystem.shutdown
只是要等待几秒钟,以便消息发送完成。
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.
和
4.
我为什么要结合第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
类如下:
学生演员
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.
这与我们在fire-n-forget写作中所看到的完全相同的代码。
TeacherActor收到QuoteRequest
消息,然后将QuoteResponse
发送回去。
教师演员
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
}
}
}
测试用例
现在,我们的测试用例将模拟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中使用调度程序,以及如何使用Kamon监视Akka应用程序。
翻译自: https://www.javacodegeeks.com/2014/10/akka-notes-actor-messaging-request-and-response-3.html