这是一个很简单的AKKA
的使用实例,功能也很简单,就是创建一个名叫example1
的actor
,然后向其发送一条消息hello akka
,而actor
在接受到消息时,将其打印出来。
object Example1 extends App {
val actorSystem : ActorSystem = ActorSystem.create("exampleSystem")
val actorRef : ActorRef = actorSystem.actorOf(Props[Example1], "example1")
actorRef ! "hello akka"
actorSystem.shutdown()
}
class Example1 extends Actor {
override def receive = {
case msg : String => println(msg)
case _ => unhandled()
}
}
这个案例虽然很简单,但使用AKKA
所需的重要组件均已出现,trait Actor
、ActorRef
、ActorSystem
,这三位是使用AKKA
必须要接触到的成员,通过继承trait Actor
来实现对消息的处理,ActorRef
则用来进行消息的发送,ActorSystem
则负责整个系统的管理。
AKKA
的世界全部都收纳在ActorSystem
中,AKKA
的世界也是色彩缤纷,本文则主要来讨论AKKA Actor
,需要注意这里说的是AKKA Actor
,而不是指上面的trait Actor
。 AKKA Actor
是我自己定义的,用于区分trait Actor
。
AKKA Actor
主要由AKKA
中的三位成员组成,分别是trait Actor
、ActorRef
、ActorCell
,其中ActorCell
这个成员还没有在上述代码中出现,后面会介绍,就知道为啥不会出现了,用下面一张图来简单说明这三者之间的关系。
图中的箭头方向,表示相互之间的拥有关系,解释如下:
ActorContext
是一个trait
,定义了一些接口,从Actor
的视角,来看ActorCell
是长什么样子;ActorCell
中同时拥有Actor
、ActorRef
的实例;ActorRef
也拥有ActorCell
的实例,这以为着ActorRef
可以通过ActorCell
来做事情;Actor
中的对象中拥有ActorRef
对象,所有在Actor
内部可以具备ActorRef
的行为;Actor
中拥有ActorContext
的实例,其实就是ActorCell
,但是只能执行ActorContext
中定义的接口;
既然这三位共同组成了AKKA Actor
,那这三位有什么分工呢?
首先来看trait Actor
,这个特质的定义如下,源码中的注释已经把删除掉。
trait Actor {
import Actor._
// 获取ActorContext实例,其实就是一个ActorCell,如果获取不到,则会抛错,
// 目的就是要求通过actorOf方法来创建Actor,避免直接通过new的方式新建Actor对象
implicit val context: ActorContext = {
val contextStack = ActorCell.contextStack.get
if ((contextStack.isEmpty) || (contextStack.head eq null))
throw ActorInitializationException(s"You cannot create an instance of [${getClass.getName}] explicitly using the constructor (new). " + "You have to use one of the 'actorOf' factory methods to create a new actor. See the documentation.")
val c = contextStack.head
ActorCell.contextStack.set(null :: contextStack)
c
}
// 这里就是获取ActorRef实例,
// 从这里可以看出,其实也是通过ActorContext获取到的,也就是获取的就是ActorCell中的ActorRef
implicit final val self = context.self
// 获取消息的发送者,也是通过ActorContext获取到的
final def sender(): ActorRef = context.sender()
// 消息被真正处理的地方
def receive: Actor.Receive
// 接下来的几个aroundXXX方法,都是对相应的XXX方法的一个包装,模板设计模式
protected[akka] def aroundReceive(receive: Actor.Receive, msg: Any): Unit = receive.applyOrElse(msg, unhandled)
protected[akka] def aroundPreStart(): Unit = preStart()
protected[akka] def aroundPostStop(): Unit = postStop()
protected[akka] def aroundPreRestart(reason: Throwable, message: Option[Any]): Unit = preRestart(reason, message)
protected[akka] def aroundPostRestart(reason: Throwable): Unit = postRestart(reason)
def supervisorStrategy: SupervisorStrategy = SupervisorStrategy.defaultStrategy
// Actor在启动前需要进行的一些初始化操作
@throws(classOf[Exception])
def preStart(): Unit = ()
// Actor在停止后需要进行的一些收尾操作
@throws(classOf[Exception])
def postStop(): Unit = ()
// Actor在故障恢复时,重启前需要进行的操作
@throws(classOf[Exception])
def preRestart(reason: Throwable, message: Option[Any]): Unit = {
context.children foreach { child ⇒
context.unwatch(child)
context.stop(child)
}
postStop()
}
// Actor在故障恢复时,重启后,对Actor进行的初始化操作
@throws(classOf[Exception])
def postRestart(reason: Throwable): Unit = {
preStart()
}
def unhandled(message: Any): Unit = {
message match {
case Terminated(dead) ⇒ throw new DeathPactException(dead)
case _ ⇒ context.system.eventStream.publish(UnhandledMessage(message, sender(), self))
}
}
}
从trait Actor
定义的接口方法来看,都是跟一个Actor
的自身行为相关的,比如
def receive: Actor.Receive
这个接口,也就是本文开头处,Example1
继承Actor
所实现的接口,是对消息进行处理的地方。
另外对于preStart
、postStop
、preRestart
、postRestart
这四个方法,则分别是用来定义一个Actor
,在启动前、关闭后、重启前、重启后,所执行的操作。其中preStart
、postStop
这两个的默认实现是空;preRestart
的默认实现则是先停掉所有的child
,然后再调用postStop
;而postRestart
的默认实现则直接就是调用preStart()
。
而aroundPreStart
、aroundPostStop
、aroundPreRestart
、aroundPostRestart
的默认实现,则是分别调用了preStart
、postStop
、preRestart
、postRestart
,并且可以看出,这里就是一个模板模式,如果实现的Actor
对这个模板不喜欢,完全可以自定义相应的aroundXXX
方法,并且在AKKA
内部,也是调用的aroundXXX
方法。
trait Actor
这个特质就是用来对Actor
的行为进行自定义的地方,这也就是为什么trait Actor
是对外开放的接口,因为消息的处理逻辑只有程序猿知道呀,所以将这些接口都开发出来,而程序猿也只需要关心消息的具体处理逻辑,不用去关心AKKA
内部是如何来实现多线程。
另外在前面说过,在Actor
中是包含了ActorRef
和ActorCell
对象的,在上面代码中,也可以看到就是self
和context
这两位,并且可以看出,self
也是通过context
获取到的。所以说,在Actor
内部,是可以通过self
给自己发送一些消息,通过context
设置一些参数等的,比如可以通过setReceiveTimeout
方法来设置多久没有收到消息,就给自己发送一个ReceiveTimeout
的消息,通过actorOf
来创建一个child
等。
看完trait Actor
,咱们再来看下ActorRef
,源码如下:
abstract class ActorRef extends java.lang.Comparable[ActorRef] with Serializable {
scalaRef: InternalActorRef ⇒
// Actor的路径
def path: ActorPath
final def compareTo(other: ActorRef) = {
val x = this.path compareTo other.path
if (x == 0) if (this.path.uid < other.path.uid) -1 else if (this.path.uid == other.path.uid) 0 else 1
else x
}
// 向Actor发送一条消息
final def tell(msg: Any, sender: ActorRef): Unit = this.!(msg)(sender)
def forward(message: Any)(implicit context: ActorContext) = tell(message, context.sender())
@deprecated("Use context.watch(actor) and receive Terminated(actor)", "2.2")
def isTerminated: Boolean
final override def hashCode: Int = {
if (path.uid == ActorCell.undefinedUid) path.hashCode
else path.uid
}
final override def equals(that: Any): Boolean = that match {
case other: ActorRef ⇒ path.uid == other.path.uid && path == other.path
case _ ⇒ false
}
override def toString: String =
if (path.uid == ActorCell.undefinedUid) s"Actor[${path}]"
else s"Actor[${path}#${path.uid}]"
}
对于ActorRef
的实例,常用的方法就是tell
或者!
这两个方法,就是用来向Actor
发送消息,从这里提供的接口可以看出,主要的任务就是用来进行消息的接受,有点类似小蜜的作用,用来接受邮件。实际使用中,常用的也就是ActorRef
,而作用则也主要是用来进消息的发送操作。
接下来来一下ActorRef
的一个子类InternalActorRef
,源码如下:
private[akka] abstract class InternalActorRef extends ActorRef with ScalaActorRef { this: ActorRefScope ⇒
// 这几个方法就是用来控制Actor的生命周期变化的
def start(): Unit
def resume(causedByFailure: Throwable): Unit
def suspend(): Unit
def restart(cause: Throwable): Unit
def stop(): Unit
// 发送一些系统消息
def sendSystemMessage(message: SystemMessage): Unit
// ActorRef的实例提供者
def provider: ActorRefProvider
// 获取parent的ActorRef
def getParent: InternalActorRef
// 获取child
def getChild(name: Iterator[String]): InternalActorRef
def isLocal: Boolean
def isTerminated: Boolean
}
这里子类仍然是一个abstract class
,主要定义了控制Actor
生命周期的操作,另外还提供了一些获取parent
、child
等接口。
再继续看下一个子类ActorRefWithCell
,源码如下:
private[akka] abstract class ActorRefWithCell extends InternalActorRef { this: ActorRefScope ⇒
def underlying: Cell
def children: immutable.Iterable[ActorRef]
def getSingleChild(name: String): InternalActorRef
}
这个子类中有一个属性underlying
,是一个Cell
的实例,前面说过,在ActorRef
中拥有ActorCell
,说的就是这个属性,接下来会看到这个属性的作用。
最后来看ActorRef
的子类LocalActorRef
的源码:
private[akka] class LocalActorRef private[akka] (
_system: ActorSystemImpl,
_props: Props,
_dispatcher: MessageDispatcher,
_mailboxType: MailboxType,
_supervisor: InternalActorRef,
override val path: ActorPath)
extends ActorRefWithCell with LocalRef {
// 获取一个ActorCell的实例,并进行初始化init操作
private val actorCell: ActorCell = newActorCell(_system, this, _props, _dispatcher, _supervisor)
actorCell.init(sendSupervise = true, _mailboxType)
// new一个ActorCell出来
protected def newActorCell(system: ActorSystemImpl, ref: InternalActorRef, props: Props, dispatcher: MessageDispatcher, supervisor: InternalActorRef): ActorCell =
new ActorCell(system, ref, props, dispatcher, supervisor)
protected def actorContext: ActorContext = actorCell
@deprecated("Use context.watch(actor) and receive Terminated(actor)", "2.2")
override def isTerminated: Boolean = actorCell.isTerminated
override def start(): Unit = actorCell.start()
override def suspend(): Unit = actorCell.suspend()
override def resume(causedByFailure: Throwable): Unit = actorCell.resume(causedByFailure)
override def stop(): Unit = actorCell.stop()
override def getParent: InternalActorRef = actorCell.parent
override def provider: ActorRefProvider = actorCell.provider
def children: immutable.Iterable[ActorRef] = actorCell.children
def getSingleChild(name: String): InternalActorRef = actorCell.getSingleChild(name)
override def getChild(names: Iterator[String]): InternalActorRef = {
@tailrec
def rec(ref: InternalActorRef, name: Iterator[String]): InternalActorRef =
ref match {
case l: LocalActorRef ⇒
val next = name.next() match {
case ".." ⇒ l.getParent
case "" ⇒ l
case any ⇒ l.getSingleChild(any)
}
if (next == Nobody || name.isEmpty) next else rec(next, name)
case _ ⇒
ref.getChild(name)
}
if (names.isEmpty) this
else rec(this, names)
}
// ========= AKKA PROTECTED FUNCTIONS =========
def underlying: ActorCell = actorCell
override def sendSystemMessage(message: SystemMessage): Unit = actorCell.sendSystemMessage(message)
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = actorCell.sendMessage(message, sender)
override def restart(cause: Throwable): Unit = actorCell.restart(cause)
@throws(classOf[java.io.ObjectStreamException])
protected def writeReplace(): AnyRef = SerializedActorRef(this)
}
从上面的源码可以看出,前面介绍的ActorRef
本身以及抽象子类中定义的接口,在这里都有了具体的实现,但是注意,所有的实现都是委托给actorCell
这个属性来操作的,从这里,也就看出了ActorRef
名称的由来,它既是一个代理,它自身不会进行任何逻辑操作,它只是代表ActorCell
对外提供一些操作接口,是对ActorCell
进行了有效的保护。
所以说,ActorRef
是ActorCell
的一个代理,它将ActorCell
中允许对外开放的接口开放给了程序猿,其实也就是进行消息发送的tell
和!
方法,而子类InternalActorRef
中定义的接口,则只能在AKKA
内部使用,并没有开放给程序猿,主要InternalActorRef
的定义中有private[akka]
,说明它并不对外开放。
AKKA Actor
的三个成员中,已经介绍完了两个,trait Actor
提供了定义Actor
行为的接口,ActorRef
则只提供了接受消息的接口,并且内部的各个方法的实现,全都是对ActorCell
的代理,感觉说了半天,没啥内容。但是事实就是如此,因为程序猿对AKKA Actor
的三个成员中,能实际接触到的就是trait Actor
和ActorRef
这两位,一个用来定义消息的处理逻辑,一个用来进行消息的发送,至于ActorCell
到底长啥样,who care。这里也是我觉得AKKA Actor
设计比较好的地方,只开放必要的接口给程序猿,开放给你的,都是你感兴趣的,你不感兴趣的,都给你很好的隐藏了起来。
再来继续看AKKA Actor
的第三位成员,也是核心所在的ActorCell
,但是在此之前,先看一下ActorContext
,源码如下,ActorContext
是ActorCell
对Actor
开放的视角,也就是在Actor
中只能看到这些接口。
trait ActorContext extends ActorRefFactory {
// 与之相关联的ActorRef
def self: ActorRef
// 通过Props获取的Actor的配置
def props: Props
// 获取当前设置的多久没有接受到消息的timeout
def receiveTimeout: Duration
// 设置多久没有接受到消息,就会发送一条ReceiveTimeout的消息,与上一个方法相对应
// 可以用来监控消息源发送消息是否正常
def setReceiveTimeout(timeout: Duration): Unit
// 下面的become和unbecome则是可以用来动态的变换Actor的行为
def become(behavior: Actor.Receive): Unit = become(behavior, discardOld = true)
def become(behavior: Actor.Receive, discardOld: Boolean): Unit
def unbecome(): Unit
// 获取当前消息的发送者
def sender(): ActorRef
// 获取所有的children
def children: immutable.Iterable[ActorRef]
// 根据name获取相应的child
def child(name: String): Option[ActorRef]
// 消息分发器,AKKA系统的线程池所在的地方
implicit def dispatcher: ExecutionContextExecutor
// actor system
implicit def system: ActorSystem
// 父节点
def parent: ActorRef
// watch和unwatch是一对,用来设定对某个ActorRef进行监控或者不监控
// 对于watch的ActorRef,在其退出时,会有相应的通知
def watch(subject: ActorRef): ActorRef
def unwatch(subject: ActorRef): ActorRef
final protected def writeObject(o: ObjectOutputStream): Unit =
throw new NotSerializableException("ActorContext is not serializable!")
}
ActorContext
还继承了ActorRefFactory
,从名称中就可以看出,是一个工厂类,主要提供了构建Actor
的各种接口,这也就是为什么在Actor
中可以通过context
来进行child的创建,这里就不列出ActorRefFactory
的源码了。
ActorContext
是ActorCell
的一个剖面,专门提供给Actor
来使用,让在Actor
中可以进行child的创建和管理。
说完ActorContext
,再来说另一个特质,也就是Cell
,也就是细胞。
private[akka] trait Cell {
// 与之关联的ActorRef、ActorSystem
def self: ActorRef
def system: ActorSystem
def systemImpl: ActorSystemImpl
// 生命周期控制的方法
def start(): this.type
def suspend(): Unit
def resume(causedByFailure: Throwable): Unit
def restart(cause: Throwable): Unit
def stop(): Unit
def isTerminated: Boolean
// 父节点
def parent: InternalActorRef
// child相关的访问接口
def childrenRefs: ChildrenContainer
def getChildByName(name: String): Option[ChildStats]
def getSingleChild(name: String): InternalActorRef
// 消息发送的各种姿势,最终都是会写入Mailbox的队列中
def sendMessage(msg: Envelope): Unit
final def sendMessage(message: Any, sender: ActorRef): Unit =
sendMessage(Envelope(message, sender, system))
def sendSystemMessage(msg: SystemMessage): Unit
def isLocal: Boolean
// 也是检查的Mailbox的队列状态
def hasMessages: Boolean
def numberOfMessages: Int
def props: Props
}
在前面说过,ActorRef
是对ActorCell
的代理,从这里的可以看出,ActorRef
中涉及的接口,基本都在这里出现了。
主要的trait
介绍完了,再来看下ActorCell
的定义,这里只看ActorCell
的定义,至于内部的各个方法的实现,这里先不做解释。
private[akka] class ActorCell(
val system: ActorSystemImpl,
val self: InternalActorRef,
final val props: Props, // Must be final so that it can be properly cleared in clearActorCellFields
val dispatcher: MessageDispatcher,
val parent: InternalActorRef)
extends UntypedActorContext with AbstractActorContext with Cell
with dungeon.ReceiveTimeout
with dungeon.Children
with dungeon.Dispatch
with dungeon.DeathWatch
with dungeon.FaultHandling {
...
}
首先看到private[akka]
,说明ActorCell
只在akka
包内可见,所以对于程序猿来说,是看不到它的,所以在使用过程中,是不会与ActorCell
直接打交道的,最多通过Actor
内部的context
属性看到ActorCell
的一个剖面。
继承的UntypedActorContext
、AbstractActorContext
,是对ActorContext
的一层继承,是为了适应Java环境下的使用,加入了一些Java下使用的API,Cell
上面也介绍过。
后面还混入了五种特征,这些特质分别实现了ActorCell
的某一方面的功能:
dungeon.ReceiveTimeout
,在介绍ActorContext
中,提到receiveTimeout
和setReceiveTimeout
方法,如何实现在设定的时间内没有收到消息,就给出一个ReceiveTimeout
的报警消息,就是这里来控制的;dungeon.Children
,是对Actor
自身的child进行管理的地方;dungeon.Dispatch
,则是利用dispatcher进行消息发送,并将mailbox注册到线程池中执行的地方;dungeon.DeathWatch
,ActorContext
中的watch
和-unwatch
的具体的实现的地方;dungeon.FaultHandling
,容错机制的实现地方。
ActorCell
作为AKKA Actor
三位成员中的核心所在,内容过于丰富,这里就简单介绍到此,后续会针对每块功能单独介绍。
总结:
trait Actor
作为行为定位中心,用户可以在这里进行当前Actor
的各种行为的定义,如消息如何处理,在启动前如果初始化,停止后如何收尾,以及故障重启时如果过度等行为;还可以通过context
属性,使用ActorCell
暴露出来的一些接口,继续动态行为的控制,child的创建,ReceiveTimeout等控制;ActorRef
,这位一位小蜜,职责很明确,就是帮ActorCell
这位老板进行邮件的接受,同时在AKKA
内部,还可以进行Actor
的启停等操作,但这些所有的操作,本质都是代替ActorCell
发号命令,最终还是得有ActorCell
来执行;ActorCell
,作为AKKA Actor
的核心所在,所有的动作,都是在其内部来实现,五脏六腑均在此处。