开发者学堂课程【Scala 核心编程 - 进阶:Actor 之间的相互通信】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/610/detail/9116
Actor 之间的相互通信
内容介绍:
一、上节小结和示意图
二、Actor 间的通讯
三、代码实现
一、上节小结和示意图
1.当程序执行 aActorRef= actorFactory.actorOf(Props[AActor], "aActor"),会完成如下任务[这是非常重要的方法]
1)actorFactory 是 ActorSystem("ActorFactory") 这样创建的。
2)这里 Props[AActor] 会使用反射机制,创建一个 AActor 对象,如果是actorFactory.actorOf(Props(new AActor(bActorRef)), "aActorRef") 形式,就是使用 new 的方式创建一个 AActor 对象,注意 Props() 是小括号。
3)会创建一个 AActor 对象的代理对象 aActorRef ,使用 aActorRef 才能发送消息
4)会在底层创建 Dispather Message,是一个线程池,用于分发消息,消息是发送到对应的 Actor 的 MailBox
5)会在底层创键 AActor 的 MailBox 对象,该对象是一个队列,可接收 Dispatcher Message发送的消息(如果消息很多,会形成队列,消息队列形成后,推送消息时是一个一个消息推送的)
6) MailBox 实现了 Runnable 接口,是一个线程,一直运行并调用 Actor 的 receive 方法,
因此当 Dispather 发送消息到 MailBox 时,Actor 在 receive 方法就可以得到信息.
7) aActorRef ! "hello",表示把hello消息发送到 AActor 的 mailbox (通过Dispatcher Message转发)
2.一个示意图的说明
二、Actor 间的通讯
1.应用实例需求
1)编写2个 Actor,分别是 AActor 和 BActor
2)AActor 和 BActor 之间可以相互发送消息,
示意图如下
3)加强对Actor传递消息机制的理解。
2.两个 Actor 的通讯录机制原理图
3.A Actor 如何发消息到 B Actor?
首先,A Actor 要给自己发消息,A Actor 作为主动发起者,必须先要持有 B Actor 的 B ActorRef,即创建 A Actor 时,首先要有 B Actor 的引用。
因为A Actor 发消息时肯定是在 receive 中发消息仍然发给 Dispatcher Message ,Dispatcher Message 会将消息发送到B Actor 的MailBox ,B Actor 的MailBox 得到消息后推送给B Actor 的 receive() 方法,receive 方法将该消息自动通过 sender() 方法得到发送过来的 Actor 的引用,sender() 方法把这条消息回送给 A Actor 的MailBox(也是经过 Dispatcher Message),然后 A Actor 的 MailBox 一直运行将消息推送给 A Actor 的 receive 方法,receive 方法接收这条消息后又给 B Actor 回消息,消息就是这样相互回应。
三、代码实现
/*新建三个文件
分别为 AActor、BBctor、ActorGame(调用前两者)
因为 actor 主要取决于主动发起消息,主动发起的一定要持有对方的引用才能发起,前面说到 A Actor 先发消息,所以先写 AActor 文件中的代码
*/
1.先写 AActor 文件中的代码
import akka . actor . { Actor,ActorRef}
class AActor(
a
ctorRef: ActorRef ) extends Actor {
va1 bActorRef: ActorRef = actorRef
override def receive: Receive = {
case "start" =>{ //
接收一个信息,开始跑程序
println( "AActor 出招了, start ok")
se
l
f ! “我打” //发给自己
}
case"我打"=>{ //接收到“我打“,输出下面的一句话
//给 BActor 发出消息
//这里需要持有BActor的引用(BActorRef)
println( "AActor(黄飞鸿)厉害
看我佛山无影脚")
bActorRef ! "我打” //给BActor发出消息
}
}
}
2.接下来在 BBctor 中写
import akka. actor.Actor
class BActor extends Actor{
override def receive : Receive = {
case “我打”=>{ //
如果接收到“我打!“
printLn( "BActor(乔峰) 挺猛
看我降龙十八掌")
//通过sender()可以获取到发
送
消息的actor 的
ref
sender() ! “我打" //
!是一种方法的重载
}
}
}
3.最后在 ActorGame 中写(如何调用)
import akka . actor . {ActorRef, Actorsystem,Props}
object ActorGame extends App {
//创建
Actorsystem
val actorfactory = Actorsystem ( "actorfactory")
//先创建
BActor 引用/代理
/
/
可以用反射机制创建
val bActorRef: ActorRef = actorfactory.actorof( Props[BActor],"bActor")
//创建
AActor 的引用
val aActorRef: ActorRef = actorfactory.actorof(Props(new AActor(bActorRef)),"aActor"')
//aActor 出
招
aActorRef ! "start" //
也可以直接发出“我打“
}
运行结果为:
发现输出结果太快,看不出效果
所以可以让其休眠一会,将AActor 文件添加部分代码如下:
case"我打"=>{
println( "AActor(黄飞鸿)厉害
看我佛山无影脚")
T
hread.sleep(1000)
bActorRef ! "我打”
}
同样,将AActor 文件添加部分代码如下:
case “我打”=>{
printLn( "BActor(乔峰) 挺猛
看我降龙十八掌")
T
hread.sleep(1000)
sender() ! “我打"
}
4.如何理解 Actor 的 receive 的方法被调用?
1)每个 Actor 对应 MailBox
2)MailBoX 实现了 Runnable 接口,处于运行的状态
3)当有消息到达 MailBox ,就会去调用 Actor 的 receive 方法,将消息推送给 receivel
在上面的程序中,发现没有显示的调用,receive 方法能被用是因为其底层有一个 MailBox 在不停的做其工作,消息一被接收到就推送,这种机制解决了同步问题,有消息就处理,没有消息也不产生影响,并且不需要关心消息到达的途径如何和消息何时回应。