Scala零基础教学【102-111】Akka 实战-深入解析
第102讲:通过案例解析Akka中的Actor运行机制以及Actor的生命周期
Actor是构建akka程序的核心基石,akka中actor提供了构建可伸缩的,容错的,分布式的应用程序的基本抽象,
actor是异步的,非阻塞式的,事件驱动的轻量级的模型,
actor中都有自己的成员,也有receive方法,在receive中处理具体的逻辑。
actor间沟通只能通过发消息。
!发消息是异步的过程,MasterActor只会对自己的邮箱感兴趣,不断循环自己的邮箱,
收到消息后进行处理。
在actor内部创建其他actor是通过ActorSystem.ActorOf(Props[MasterActor])
创建出工作的actor后,actor就进入start状态,开始查看邮箱中是否有邮件,
有邮件时就处理邮件,直到收到停止的指令。
Actor的生命周期:
1)初始化:preStart
2)接收消息、处理消息。用模式匹配的方式匹配到消息就处理
3)stop处理消息进行清理工作。
为什么会出现preRestart/postRestart呢?
如actor崩溃了(exception),被supervisor重新启动时,preRestart/postRestart就会被调用,进行状态恢复
创建actor有多种方式,如ActorSystem,接下来讲解其他方式。
第103讲:通过案例解析Akka中的Actor的定义和创建
第104讲:通过案例解析Akka中的Actor的不同类型的Constructor
actor的构造器有两种:
1默认:构造时把actor具体的类名称传入
2非:构造实例时传入参数,助于特殊初始化,
如可以在构造器中传入其他actor的actorRef(引用),这样就可以在自己的receive中
给其他actor发消息。
val aggregateActor:ActorRef = context.actorOf(Props[AggregateActor],name="aggregate")
构造默认构造器
val reduceActor:ActorRef = context.actorOf(Props(new ReduceActor(aggregateActor)),name="reduce")
构造非默认构造器
源码:
/**
* Strores the context for this actor, including self , and sender.
* It is implicit to support operations such as 'forward'.
*/
protected[akka] implicit val context:ActorContext = {
val contextStack = ActorCell.contextStack.get
...
}
implicit final val self = context.self //MUST BE A VAL, TRUST ME
使用context.actorOf可以构建具体的actor的实例并拿到实例的句柄
def actorOf(props:Props,name:String):ActorRef
第105讲:解析Akka中的子Actor及其代码
akka中为什么会有子actor
因为akka中的actor是分层结构的,任何actor在akka中都属于具体的层次。
为什么需要子actor,什么时候需要应该创建子actor
1当任务有多个子任务时
2某actor由父acotr执行时可能 比较容易出错,将具体任务进行隔离。
因为使用子actor时,当子actor崩溃时,可通过监控actor进行恢复
当然通过actor创建了子actor,子actor可将任务委派给下一个actor,
可能有人认为创建actor会占用巨大资源,事实上,基于优良的架构,
创建acotr代价远远小于线程的创建。
val _system = ActorSystem("HelloAkka") //通过actorSystem创建具体的actor的容器:HelloAkka
val master = _system.actorOf(Props[MasterActor],name="master" //master是HelloAkka的子actor。
println(master.path)
上述代码打印结果:
akka://HelloAkka/user/master
akka://是固定的协议,与http协议一样。
HelloAkka是创建的actorSystem的具体的名称
user是根目录下创建的所有actor的上一级目录,除user外还有system
可以看出akka是层级结构。
val aggregateActor:ActorRef = context.actorOf(Props[AggregateActor],name="aggregate")
val reduceActor:ActorRef = context.actorOf(Props(new ReduceActor(aggregateActor)),name="reduce")
val mapActor:ActorRef = context.actorOf(Props(new MapActor(reduceActor)),name="map")
println(aggregateActor.path)
println(reduceActor.path)
println(mapActor.path)
打印结果:
akka://HelloAkka/user/master/aggregate
akka://HelloAkka/user/master/reduce
akka://HelloAkka/user/master/map
第106讲:解析Akka中的消息的不同发送方式方式详解学习笔记
akka中有两种消息发送方式:
1.发送后立即忘记(Fire and Forget)
2.发送消息后等待目标actor回复(Send and Receive)
所有消息都是immutable(不可变的),异步的。
发送消息的方法:java是tell,scala是!
第107讲:Akka中的Future使用代码实战详解
本讲分享akka中消息发送的第二种方式:send and receive
给actor发送消息后会等待目标actor的回复
用future等待目标actor的回复
actorA ask actorB,用future来接收actorB的内容。
接收到future后可以提取出future的内容。
FutrueActor
package akka.dt.app.actors import akka.actor.Actor /** * 描述: * 作者: sunrunzhi * 时间: 2018/11/15 14:18 */ class FutureActor extends Actor{ def receive:Receive={ case message:String=>{ Thread.sleep(3000) println(Thread.currentThread().getName+" is going to work!!!") println("The content of the received message is :"+message) this.sender() ! "Power comes from Love!"+this.self println("The sender path="+this.sender().path) context.stop(this.self) println(this.self.path) } } }
ActorWithFuture:
package akka.dt.app.future import akka.actor.{ActorRef, ActorSystem, Props} import akka.dt.app.actors.FutureActor import akka.pattern.Patterns import akka.util.Timeout import scala.concurrent.duration.Duration import scala.concurrent.{Await, Future} /** * 描述:ActorWithFuture * 作者: sunrunzhi * 时间: 2018/11/15 13:58 */ object ActorWithFuture { def main(args: Array[String]): Unit = { println(Thread.currentThread().getName) //创建ActorSystem容器 val Fsystem=ActorSystem("FutrueSystem") val worker:ActorRef=Fsystem.actorOf(Props[FutureActor],name="future") //等待超时时间 val timeout:Timeout=new Timeout(Duration.create(5,"second")) //通过ask发消息到worker,等待worker回复内容 val futrue:Future[AnyRef]=Patterns.ask(worker,"For free, for everyone,forever,for love",timeout) //通过Await.result的等待future结果 val result=Await.result(futrue,timeout.duration) println("result:"+result) } }
第108讲:Akka中的Actor消息的处理、消息回复、消息转发
本讲主要讲解:
1.Actor消息的处理
2.Actor消息的回复
3.Actor消息的转发
处理消息一般通过复写receive方法,通过模式匹配的方式。
/**
* This defines the initial actor behavior, it must return a partial function
* with the actor logic.
*/
Protected def receive: Receive
如果要回复消息给发送者的话要获得发送者的actorRef,
就是说代理的句柄。Akka中提供了sender这个ActorRef指向当前收到的消息的发送者。但这有一个风险:消息到当前actor后,它的发送者已不存在了,这时给sender回复消息时,此时的消息就会变成deadLetters。
/**
* The reference sender Actor of the last received message.
* Is defined if the message was sent from another Actor,
* else ‘deadLetters’ in [[akka.actor.ActorSystem]].
*/
Final def sender: ActorRef = context.sender
消息的转发:
Forward的方式。
mapActor.forward(message)
Forward的源码:
/**
* Forwards the message and passer the original sender actor as the sender.
* Works with ! And ?/ask
Def forward(message: Any)(implicit context: ActorContext) = tell(message,context.sender)
第109讲:Akka中的Actor异步和同步停止方式实战详解学习笔记
本讲内容:
1.Actor异步停止方式
2.Actor同步停止方式
为什么要停止actor?
Actor完成了自己的消息处理任务后,后续不再需要这个actor再继续处理工作,为了节省或回收系统资源,就有必须停止actor
Actor在正式处理邮箱中具体的邮件以前会有preStart,在preStart中进行资源的初始化,如连接网络或连接数据库或打开文件。在处理完毕邮件后,actor生命周期的最后会有环节,会有postStop,在postStop中可以进行关闭网络关闭数据库或关闭文件的句柄,在akka中要想停止 actor有多种方式:同步、异步,但都需要postStop,在postStop中进行资源回复的操作。
在postStop调用时会在内部在akka继承树上产生操作,任务完成后邮箱中的内容不会再被处理,同时会有一个具体消息叫deathWatch的消息发出,通知supervisor说自己停止了。在akka中要停止actor会有多种不同方式:
1.停止actorSystem,
/**
* Stop this actor system.This will stop the guardian actor, which in turn will recurisiverly stop all its child actor, then the system guardian (below which the logging actor reside) and the execute all registered termination handlers (See [[ActorSystem.registerOnTermination]]).
*/
Def shutdown(): Unit
停止一个actor后,其子actor会全部被停止。
2.其他方式停止actor:异步操作。
Master ! Poisonpill //异步
Master ! Kill //同步
_system.shutdown
Context.stop(self) //也可以停止其他actor。
第110讲:Akka中的Actor的Monitoring初步解析
本讲主要讲解actor的监控机制
这和supervisor不同。
Akka是由树状结构构成的分层结构。
下层actor发生变化上层actor都可以感知到变化。
假设stop上层actor时,下层actor会首先被stop,然后才是上层actor。
Monitor actor会监控worker actor,如果worker actor被中断,monitor actor会感知到这个事件,并采取适当的action,如再次启动一个worker actor实例,继续完成同样的工作。
这在实际开发系统时非常重要,开发系统,具体actor可能会工作不稳定,或硬件软件因素被意外或意料中的停止,而被中断的工作还要继续,所以需要monitor actor监控。
Monitor actor是通过context.watch来监控worker actor的。
当worker actor被停止时,会有terminated消息发送给monitor actor,monitor actor收到消息时会采取一定措施。如启动一个同样的actor。这样其他actor就可以继续给这个actor发消息了。
第111讲:Akka中的Actor中用become和unbecome动态切换receive的具体处理逻辑实战
本期内容:
1.动态切换actor处理逻辑分析
2.Become和unbecome代码实战
可以改变actor具体处理业务逻辑的代码,而且是动态改变。
可以借助akka提供的become和unbecome机制动态地切换当前actor的处理逻辑。
代码:
Case class Spark
Case class Flink
Class SparkFlinkActor extends Actor {
import context._
var cont = 0
def receive: Receive = {
case Spark =>
println(“Here is Spark!!!”)
count = count + 1
Thread.sleep(100)
self ! Flink //receive在收到Flink消息后就会直接进入become内
become { //把处理代码切换成become内的逻辑
//become之前的代码在become出现后就不再存在了
case Flink =>
println(“Here is Flink!!!”)
count = count +1
Thread.sleep(100)
self ! Spark
unbecome() //把代码处理逻辑切换一次,把become代码从堆栈中弹出。
}
if(count > 10) countext.stop(self)
}
}
object MyActorSystem {
def main(args: Array[String]): Unit = {
val _system = ActorSystem(“BecomeUnbecome”)
val sparkFlinkActor = _system.actorOf(Props[SparkFlinkActor])
sparkFlinkActor ! Spark
Thread.sleep(2000)
_system.shutdown
}
}