从Akka Notes的介绍性第一部分中,我们在Akka Toolkit中看到了Actor的鸟瞰图。 在Akka Notes的第二部分中,我们将研究Actors的消息传递部分。 对于该示例,我们将使用前面讨论的相同的Student-Teacher示例。
在Actor Messaging的第一部分中,我们将创建Teacher Actor,而不是Student Actor,将使用一个名为StudentSimulatorApp
的主程序。
再探学生老师
现在,让我们考虑一下StudentSimulatorApp仅发送给TeacherActor的消息。 当我说StudentSimulatorApp
,我只是说一个普通的主程序。
图片传达了这一点:
(如果您的条款过于笼统,请放心,我们将详细介绍这些条款)
- 学生创建一个称为
ActorSystem
东西 - 它使用ActorSystem创建称为
ActorRef
。QuoteRequest
消息发送到ActorRef(TeacherActor的代理) - Actor ref将消息传递到
Dispatcher
- 分派器将消息放入目标Actor的
MailBox
。 - 然后,分派器将
Mailbox
放在线程上(在下一节中有更多介绍)。 -
MailBox
使消息出队,并最终将其委派给实际的教师演员的接收方法。
就像我说的,不用担心。 现在让我们详细了解每个步骤。 完成后,您可以返回并重新访问这五个步骤。
我们将使用此StudentSimulatorApp来启动JVM并初始化ActorSystem。
从图片中我们可以看出,StudentSimulatorApp
- 创建一个Actor系统
- 使用ActorSystem创建教师演员(ActorRef)的代理
- 将QuoteRequest消息发送到代理。
现在让我们仅关注这三点。
创建一个Actor系统
为TeacherActor创建代理?
发送
QuoteRequest
到代理
ActorSystem是ActorWorld的入口点。 您可以通过ActorSystems创建和停止Actor。 甚至关闭整个Actor环境。
另一方面,Actor是分层的,ActorSystem也类似于java.lang.Object
或scala.Any
适用于所有Actor,这意味着它是所有Actor的根。 使用ActorSystem的actorOf
方法创建Actor时,将在ActorSystem下方创建一个Actor。
初始化ActorSystem的代码如下:
val system=ActorSystem("UniversityMessageSystem")
UniversityMessageSystem
只是您为ActorSystem赋予的可爱名称。
让我们考虑以下代码段:
val teacherActorRef:ActorRef=actorSystem.actorOf(Props[TeacherActor])
actorOf
是actorOf
中Actor的创建方法。 但是,正如您所看到的,它没有返回我们需要的TeacherActor。 它返回ActorRef
类型的ActorRef
。
ActorRef
充当实际Actor的代理。 客户不直接与演员交谈。 为此,Actor模型避免了直接访问TeacherActor或任何Actor
中的任何自定义/私有方法或变量的方式。
要重复,您只将消息发送到ActorRef,最终它会到达您的实际Actor。 您永远不能直接与演员交谈。 如果您发现一些卑鄙的方法,人们会恨死您。
这又是一个班轮。 您只需将QuoteRequest
消息tell
ActorRef。 Actor中的tell方法实际上是!
。 (ActorRef中还有一个tell
方法,它将调用委托给!
)。
//send a message to the Teacher Actor
teacherActorRef!QuoteRequest
而已 !!!
如果您认为我在撒谎,请检查以下StudentSimulatorApp
的完整代码:
StudentSimulatorApp.scala
package me.rerun.akkanotes.messaging.firenforget
import akka.actor.ActorSystem
import akka.actor.Props
import akka.actor.actorRef2Scala
import me.rerun.akkanotes.messaging.protocols.TeacherProtocol._
object StudentSimulatorApp extends App{
//Initialize the ActorSystem
val actorSystem=ActorSystem("UniversityMessageSystem")
//construct the Teacher Actor Ref
val teacherActorRef=actorSystem.actorOf(Props[TeacherActor])
//send a message to the Teacher Actor
teacherActorRef!QuoteRequest
//Let's wait for a couple of seconds before we shut down the system
Thread.sleep (2000)
//Shut down the ActorSystem.
actorSystem.shutdown()
}
好吧,我有点作弊。 您必须shutdown
ActorSystem,否则JVM会一直运行。 而且我正在使主线程休眠一会儿,只是为了让TeacherActor完成任务。 我知道这听起来很愚蠢。 不用担心 为了避免这种攻击,我们将在下一部分中编写一些简洁的测试用例。
讯息
我们只是将一个QuoteRequest
告诉了QuoteRequest
,但我们根本没有看到消息类!
它来了 :
(建议将消息包装在一个不错的对象中,以方便组织)
教师协议
package me.rerun.akkanotes.messaging.protocols
object TeacherProtocol{
case class QuoteRequest()
case class QuoteResponse(quoteString:String)
}
如您所知, QuoteRequest
是针对来到TeacherActor的请求的。 Actor将使用QuoteResponse
响应。
调度程序和邮箱
ActorRef
将消息处理功能委托给Dispatcher
。 在ActorSystem
,当我们创建ActorSystem
和ActorRef
,创建了Dispatcher
和MailBox
。 让我们看看它们的含义。
邮箱
Ever Actor拥有一个MailBox(我们将在以后看到一种特殊情况)。 根据我们的类比,每个老师也有一个邮箱。 教师必须检查邮箱并处理消息。 在Actor世界中,情况恰恰相反–邮箱,当有机会时,可以使用Actor完成其工作。
此外,邮箱还有一个队列以FIFO方式存储和处理消息-与我们的常规收件箱稍有不同,在常规收件箱中,最新的是顶部的收件箱。
现在,调度员
Dispatcher做一些非常酷的事情。 从外观上看,Dispatcher只是从ActorRef获取消息并将其传递到邮箱。 但是幕后发生了一件令人惊奇的事情:
分派器包装ExecutorService (ForkJoinPool或ThreadPoolExecutor)。 它针对此ExecutorService执行MailBox
。
从Dispatcher中查看以下片段:
protected[akka] override def registerForExecution(mbox: Mailbox, ...): Boolean = {
...
try {
executorService execute mbox
...
}
什么? 您只是说要执行邮箱吗?
对。 我们已经看到邮箱将所有消息保存在队列中。 另外,由于执行程序运行MailBox
,因此邮箱必须是Thread
。 你是对的。 这几乎是MailBox的声明和构造函数。
这是邮箱的签名:
private[akka] abstract class Mailbox(val messageQueue: MessageQueue) extends SystemMessageQueue with Runnable
教师演员
MailBox
触发其run
方法时,会将其从消息队列中出队,然后将其传递给Actor
进行处理。
将消息tell
ActorRef
时最终被调用的方法是目标Actor的receive
方法。
TeacherActor是一个基本类,它具有引号List
以及很明显的处理消息的receive
方法。
看一下这个 :
教师演员
package me.rerun.akkanotes.messaging.firenforget
import scala.util.Random
import akka.actor.Actor
import me.rerun.akkanotes.messaging.protocols.TeacherProtocol._
/*
* Your Teacher Actor class.
*
* The class could use refinement by way of
* using ActorLogging which uses the EventBus of the Actor framework
* instead of the plain old System out
*
*/
class TeacherActor extends Actor {
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)))
println (quoteResponse)
}
}
}
TeacherActor的接收方法模式仅与一条消息匹配QuoteRequest
(实际上,对默认情况进行模式匹配是一种很好的做法,但是有一个有趣的故事要讲)
接收方法所做的就是
-
QuoteRequest
模式匹配 - 从静态报价单中选择随机报价
- 构造
QuoteResponse
- 将QuoteResponse打印到控制台
码
- 整个项目可以从github此处下载。
我们将在接下来的部分中介绍更多有趣的内容……
翻译自: https://www.javacodegeeks.com/2014/09/akka-notes-actor-messaging-1.html