开发者学堂课程【Scala 核心编程 - 进阶:Actor 模型快速入门案例】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/610/detail/9115
Actor 模型快速入门案例
内容介绍:
一、Actor 模型工作机制说明(复习)
二、应用实例需求
三、SayHelloActor 项目
四、代码实现
一、Actor 模型工作机制说明(复习)
1.Actor 间传递消息机制(对照工作机制示意图理解)
1)每一个消息就是一个 Message 对象。Message 继承了 Runable ,因为 Message 就是线程类。
2)从 Actor 模型工作机制看上去很麻烦,但是程序员编程时只需要编写 Actor 就可以了,其它的交给 Actor 模型完成即可。
3) A Actor 要给 B Actor 发送消息,那么 A Acto r 要先拿到(也称为持有) B Actor的代理对象 ActorRef 才能发送消息。
二、应用实例需求
1.编写一个 Actor,比如 SayHelloActor
2.SayHelloActor 可以给自己发送消息,如图
3.要求使用 Maven 的方式来构建项目,这样可以很好的解决项目开发包的依赖关系。
在进行 AKKA 开发时,要下载 AKKA 的开发包,自己下载比较麻烦,而且 Scala 的版本和 AKKA 有对应关系,如果 Scala 的版本和 AKKA 对应不上,则无法做开发,此时用 Maven 是最好的方式。
Actor 自我通讯机制原理图
1.初步理斛 Actor 通计机制
对上图的理解:
A Actor, A 有一个 ActorRef,通过 A Actor 发一个消息到 A Actor 的MailBox ,相当于A Actor 拿到消息,A ActorRef 引用,发消息到 Dispatcher Message,然后 Dispatcher Message 再将消息转发给 A Actor 里面的MailBox。MailBox 会将消息推送给 A Actor 里面的 receive 方法,并且给自己 A ActorRef 发送消息也会启动 Actor 的运行
三、SayHelloActor 项目
1.创建项 new->new Project ->选择Maven
2.给项目命名
3.下一步->finish
4.会生成 pom.xml 文件( maven文件,项目包的依赖)
5.将下面的 maven 配置模板拷贝到 pom.xml 文件中
<!--定义一
个
常量-->
<properties>
<encoding>UTF-8</encoding>
<scala.version>2.11.8</scala.version>
<scala.compat.version>2.11</scala.compat.version>
<akka.version>2.4.17</akka.version>
</properties>
<dependencies>
<!--添加scala的依赖--->
<dependency>
<groupld>org.scala-lang</groupld>
<artifactld>scala-library</artifactld>
<version>$[scala.version}/version>
</dependency>
<!--添加akka 的 actor依赖-->
<dependency>
<groupld>com.typesafe.akka</groupld>
<artifactld>akka-actor_${scala.compat.version}</artifactld>
<version>${akka.version}</version>
</dependency>
<!--多进程之间的Actor通信-->
<dependency>
<groupld>com.typesafe.akka</groupld>
<artifactld>akka-remote_${scala.compat.version}</artifactld>
<version>${akka.version}/version>
</dependency>
</dependencies>
<!--指定插件-->
<build>
<!-指定源码包和测试包的位置-->
<sourceDirectory>src/main/scala</sourceDirectory>
/*这里 scala 会有错误提示,因为目前没有在 main 中建一个 scala,此时要打开源码包,找到 main 新建一个目录叫 scala,然后在 scala 中找到 Mark Directory as 标记成 Sources Root ,继续再在 text 中新建一个 scala ,然后也将其标记为 Sources Root。此时会有提示,
如图:
点击引入改包,第一次下载速度较慢,需要10分钟到20分钟不等
*
/
<testSourceDirectory>src/test/scala</testSourceDirectory> //
这里
scala
会有错误提示
<plugins>
<!--指定编译scala的插件-->
<plugin>
<groupld>net.alchim31.maven</groupld>
<artifactld>scala-maven-plugin</artifactld>
<version>3.2.2</version>
<executions>
<execution>
goals>
<goal>compile</goal>
工
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</ args>
< / configuration>
< / execution>
</ executions>
</ plugin>
<!-- maven打包的插件-->
<plugin>
<groupld>org.apache.maven.plugins</groupld>
<artifactlas-maven-shade-plugin</artifactld>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact><excludes>
exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
resource>reference.conf</resource>
</transformer>
--指定main方法-→>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClas s>xxx</ mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
6.因为按照配置模板的内容"指定源码包和测试包的位置”的部分<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
我们需要创建对应的 scala 目录,并 mark 为 sources root
7.当修改后,第一次速度比较慢,因为 maven 需要 resolve 包的依赖,要下载相关的包
注意:
如果下载相关包时无响应,需要如图勾选,update snapshots,而且不需要联网,如果使用 maven 解决依赖后,仍然 pom.xml 有误,则只需要重启下 idea,或者动一下 pom.xml 文件(不用改),重新保存即可.
上述准备完成后,可以做 scala 基本开发(如果想再加一个 scala 框架,发现无该选项,因为 maven 中已经将 scala 包引入了)
四、代码实现
import akka.actor . {Actor,Actorsystem}
//说明
//1.当我们继承 Actor 后,就是一个Actor ,核心方法 receive 方法要重写
class sayHelloActor extends Actor{
/*说明
1. receive 方法,会被该 Actor 的MaiLBox (实现了Runnable接口)调用
2.当该 Actor 的 MaiLBox 接收到消,息,就会调用receive(每个 Actor 都有一个邮箱)
3. type Receive = PartiaLFunction[ Any, unit] ,Receive 返回偏函数
*
/
override def receive :Receive = {
case "hel1o" =>printLn("收到hello,回应hello too")
case "ok" =>println("收到
ok
,回应ok too")
case _=println("匹配不到")
}
}
object sayHelloActorDemo {
//1.先创建—个 Actorsystem ,专门用于创建 Actor
private val actocyFactory = Actorsystem( "actoryFactory")
//2.创建一个 Actor 的同时,返回 Actor 的 ActorRef
/*
说明
(1)Props[SayHelloActor ] 创建了一个 sayHelloActor 实例,底层使用反射机制
(2)“sayHelloActor” 给 actor取名(注意名字不要重复)
(3)sayHelloActorRef:ActorRef 就是 Props[SayHelloActor ] 的ActorRef
(4)创建的 sayHelloActorRef:ActorRef 的实例,被 Actorsystem 接管
*
/
private val sayHelloActorRef:ActorRef=actoryFactory.actorof
(Props[SayHelloActor ] , " sayHelloActor")
def
main (arg:Array[S
tring
]):Unit)={
/
/
给
sayHelloActor
发消息(邮箱)
sayHelloActorRef
!
”hello” //
这里写的是谁就发给谁
}
}
运行结果为:
收到 hello,回应 hello too
注意:
在创建 sayHelloActorRef:ActorRef 对象时,[SayHelloActor ] 已经被创建,只是被 Actorsystem 接管了
sayHelloActorRef 不是一个真正的实例,只是跟[SayHelloActor ] 关联了,没有创建一个新的对象,下图以便理解:
在底层有一个SayHelloActor 实例,同时它将 sayHelloActorRef 引用,这个引用不理解为一个全新的东西,它只是为了和 SayHelloActor 关联。
然后发出消息这个动作,先把消息发给
Dispatcher Message 消息分发器,消息分发器会将消息打包,将消息序列化,然后将消息发给 sayHelloActor 底层对应的邮箱,MailBox 是 Runable 的,可以一直处于运行状态。
MailBox 接收消息后,要将消息推送给 SayHelloActor 实例,因为在 MailBox 中是有SayHelloActor 的实例,该实例中有一个 receiver 方法,接收到消息后会调用 receiver 方法。
调用该方法可以理解为将消息推送给 SayHelloActor 实例中的方法。
部分代码:
def
main (arg:Array[S
tring
]):Unit)={
sayHelloActorRef
!
”hello”
}
将该部分代码改为
def
main (arg:Array[S
tring
]):Unit)={
sayHelloActorRef
!
”hello”
sayHelloActorRef
!
”
ok
”
}
运行结果为
收到 hello,回应 hello too
收到 ok,回应 ok too
(输出是有序的)
如果输出一个“ok~“,则会输出”匹配不到“
运行发现,程序并没有退出,因为底层的 MailBox 是 Runable,没有手动停止则程序会一直运行,如果想要终止或退出,将下面代码改写:
override def receive :Receive = {
case "hel1o" =>printLn("收到hello,回应hello too")
case "ok" =>println("收到
ok
,回应ok too")
case _=println("匹配不到")
}
改写为:
override def receive :Receive = {
case "hel1o" =>printLn("收到hello,回应hello too")
case "ok" =>println("收到
ok
,回应ok too")
c
ase “exit”=>{
p
rintln(“
接收到 exit
指令,退出系统
”)
con
text.stop(self) //
停止 actoref
c
ontext.system.terminate() //
退出 actory
case _=println("匹配不到")
}
执行该指令
def
main (arg:Array[S
tring
]):Unit)={
sayHelloActorRef
!
”
exit
”
}