AKKA-源码-Actor的结构设计

这是一个很简单的AKKA的使用实例,功能也很简单,就是创建一个名叫example1actor,然后向其发送一条消息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 ActorActorRefActorSystem,这三位是使用AKKA必须要接触到的成员,通过继承trait Actor来实现对消息的处理,ActorRef则用来进行消息的发送,ActorSystem则负责整个系统的管理。

AKKA的世界全部都收纳在ActorSystem中,AKKA的世界也是色彩缤纷,本文则主要来讨论AKKA Actor,需要注意这里说的是AKKA Actor,而不是指上面的trait ActorAKKA Actor是我自己定义的,用于区分trait Actor

AKKA Actor主要由AKKA中的三位成员组成,分别是trait ActorActorRefActorCell,其中ActorCell这个成员还没有在上述代码中出现,后面会介绍,就知道为啥不会出现了,用下面一张图来简单说明这三者之间的关系。

这里写图片描述

图中的箭头方向,表示相互之间的拥有关系,解释如下:

  1. ActorContext是一个trait,定义了一些接口,从Actor的视角,来看ActorCell是长什么样子;

  2. ActorCell中同时拥有ActorActorRef的实例;

  3. ActorRef也拥有ActorCell的实例,这以为着ActorRef可以通过ActorCell来做事情;

  4. Actor中的对象中拥有ActorRef对象,所有在Actor内部可以具备ActorRef的行为;

  5. 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所实现的接口,是对消息进行处理的地方。

另外对于preStartpostStoppreRestartpostRestart这四个方法,则分别是用来定义一个Actor,在启动前、关闭后、重启前、重启后,所执行的操作。其中preStartpostStop这两个的默认实现是空;preRestart的默认实现则是先停掉所有的child,然后再调用postStop;而postRestart的默认实现则直接就是调用preStart()

aroundPreStartaroundPostStoparoundPreRestartaroundPostRestart的默认实现,则是分别调用了preStartpostStoppreRestartpostRestart,并且可以看出,这里就是一个模板模式,如果实现的Actor对这个模板不喜欢,完全可以自定义相应的aroundXXX方法,并且在AKKA内部,也是调用的aroundXXX方法。

trait Actor这个特质就是用来对Actor的行为进行自定义的地方,这也就是为什么trait Actor是对外开放的接口,因为消息的处理逻辑只有程序猿知道呀,所以将这些接口都开发出来,而程序猿也只需要关心消息的具体处理逻辑,不用去关心AKKA内部是如何来实现多线程。

另外在前面说过,在Actor中是包含了ActorRefActorCell对象的,在上面代码中,也可以看到就是selfcontext这两位,并且可以看出,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生命周期的操作,另外还提供了一些获取parentchild等接口。

再继续看下一个子类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进行了有效的保护。

所以说,ActorRefActorCell的一个代理,它将ActorCell中允许对外开放的接口开放给了程序猿,其实也就是进行消息发送的tell!方法,而子类InternalActorRef中定义的接口,则只能在AKKA内部使用,并没有开放给程序猿,主要InternalActorRef的定义中有private[akka],说明它并不对外开放。


AKKA Actor的三个成员中,已经介绍完了两个,trait Actor提供了定义Actor行为的接口,ActorRef则只提供了接受消息的接口,并且内部的各个方法的实现,全都是对ActorCell的代理,感觉说了半天,没啥内容。但是事实就是如此,因为程序猿对AKKA Actor的三个成员中,能实际接触到的就是trait ActorActorRef这两位,一个用来定义消息的处理逻辑,一个用来进行消息的发送,至于ActorCell到底长啥样,who care。这里也是我觉得AKKA Actor设计比较好的地方,只开放必要的接口给程序猿,开放给你的,都是你感兴趣的,你不感兴趣的,都给你很好的隐藏了起来。


再来继续看AKKA Actor的第三位成员,也是核心所在的ActorCell,但是在此之前,先看一下ActorContext,源码如下,ActorContextActorCellActor开放的视角,也就是在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的源码了。

ActorContextActorCell的一个剖面,专门提供给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的一个剖面。

继承的UntypedActorContextAbstractActorContext,是对ActorContext的一层继承,是为了适应Java环境下的使用,加入了一些Java下使用的API,Cell上面也介绍过。

后面还混入了五种特征,这些特质分别实现了ActorCell的某一方面的功能:

  • dungeon.ReceiveTimeout,在介绍ActorContext中,提到receiveTimeoutsetReceiveTimeout方法,如何实现在设定的时间内没有收到消息,就给出一个ReceiveTimeout的报警消息,就是这里来控制的;

  • dungeon.Children,是对Actor自身的child进行管理的地方;

  • dungeon.Dispatch,则是利用dispatcher进行消息发送,并将mailbox注册到线程池中执行的地方;

  • dungeon.DeathWatchActorContext中的watch和- unwatch的具体的实现的地方;

  • dungeon.FaultHandling,容错机制的实现地方。

ActorCell作为AKKA Actor三位成员中的核心所在,内容过于丰富,这里就简单介绍到此,后续会针对每块功能单独介绍。


总结:

  1. trait Actor作为行为定位中心,用户可以在这里进行当前Actor的各种行为的定义,如消息如何处理,在启动前如果初始化,停止后如何收尾,以及故障重启时如果过度等行为;还可以通过context属性,使用ActorCell暴露出来的一些接口,继续动态行为的控制,child的创建,ReceiveTimeout等控制;

  2. ActorRef,这位一位小蜜,职责很明确,就是帮ActorCell这位老板进行邮件的接受,同时在AKKA内部,还可以进行Actor的启停等操作,但这些所有的操作,本质都是代替ActorCell发号命令,最终还是得有ActorCell来执行;

  3. ActorCell,作为AKKA Actor的核心所在,所有的动作,都是在其内部来实现,五脏六腑均在此处。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值