Akka中actor的创建过程

Akka中actorOf方法分析

一直以来都对Akka中的actor创建过程有点迷糊,说是知道但是又有所不清楚,这次有空把源码翻了一下,顺便记录下自己的理解,有错误的地方还请指正

actorOf方法

大家应该都知道一般我们要创建actor都要去调用context.actorOf方法或者actorSystem.actorOf这两种方法,其中actorSystem创建的actor会直接挂在user路径下,也是用户可以创建的最高层次的actor节点。通过观察源码发现,actorContext和actorSystem都继承了ActorRefFactory,这里是真正发起actorOf方法的所在地

abstract class ActorSystem extends ActorRefFactory{...}
trait ActorContext extends ActorRefFactory{...}   //ActorContext 就是我们在actor中可以直接调用的context对象

先看一下actorOf的方法定义

 def actorOf(system: ActorSystemImpl, props: Props, supervisor: InternalActorRef, 
				  path: ActorPath,systemService: Boolean, deploy: Option[Deploy], 
				  lookupDeploy: Boolean, async: Boolean): InternalActorRef 

几个参数含义大概如下

  • system和props无须赘言,自然就是我们创建actor必备的那两个参数
  • supervisor是该actor的监督者,也就是父亲,ActorPath是actor的路径,deploy是actor的配置
  • 剩下的几个参数都是一些flag,是否为系统actor以及是否异步等

case NoRouter ⇒ blabla…
case router ⇒blabla…
进入方法有2个case条件,一个是NoRouter一个是router,顾名思义,不再解释太多,下面重点看下NoRouter的情况。
首先是解析deploy,用deploy中定义的mailbox和dispatcher去覆盖props中的值

val props2 =
          // mailbox and dispatcher defined in deploy should override props
          (if (lookupDeploy) deployer.lookup(path) else deploy) match {
            case Some(d) ⇒
              (d.dispatcher, d.mailbox) match {
                case (Deploy.NoDispatcherGiven, Deploy.NoMailboxGiven) ⇒ props
                case (dsp, Deploy.NoMailboxGiven)                      ⇒ props.withDispatcher(dsp)
                case (Deploy.NoMailboxGiven, mbx)                      ⇒ props.withMailbox(mbx)
                case (dsp, mbx)                                        ⇒ props.withDispatcher(dsp).withMailbox(mbx)
              }
            case _ ⇒ props // no deployment config found
          }

接下来便是正式的创建dispatcher和mailbox过程了

val dispatcher = system.dispatchers.lookup(props2.dispatcher)
val mailboxType = system.mailboxes.getMailboxType(props2, dispatcher.configurator.config)
if (async) 
	new RepointableActorRef(system, props2, dispatcher, mailboxType, supervisor, path)
	.initialize(async)
else 
	new LocalActorRef(system, props2, dispatcher, mailboxType, supervisor, path)
首先说下val dispatcher = system.dispatchers.lookup(props2.dispatcher)

这里的dispatchers其实是我们配置中定义的那些dispatcher配置,不难猜到就是解析配置文件再使用反射去创建对应的dispatcher,可以简单参阅一下创建代码,分别解析dispatcher的type去创建不同类型的dispatcher

cfg.getString("type") match {
      case "Dispatcher" ⇒ new DispatcherConfigurator(cfg, prerequisites)
      case "BalancingDispatcher" ⇒

        throw new IllegalArgumentException("BalancingDispatcher is deprecated, use a BalancingPool instead. " +
          "During a migration period you can still use BalancingDispatcher by specifying the full class name: " +
          classOf[BalancingDispatcherConfigurator].getName)
      case "PinnedDispatcher" ⇒ new PinnedDispatcherConfigurator(cfg, prerequisites)
      case fqn ⇒
        val args = List(classOf[Config] → cfg, classOf[DispatcherPrerequisites] → prerequisites)
        prerequisites.dynamicAccess.createInstanceFor[MessageDispatcherConfigurator](fqn, args).recover({
          case exception ⇒
            throw new ConfigurationException(
              ("Cannot instantiate MessageDispatcherConfigurator type [%s], defined in [%s], " +
                "make sure it has constructor with [com.typesafe.config.Config] and " +
                "[akka.dispatch.DispatcherPrerequisites] parameters")
                .format(fqn, cfg.getString("id")), exception)
        }).get
    }
接下来我们再看system.mailboxes.getMailboxType(props2, dispatcher.configurator.config)

这里的mailboxType名字比较令人费解,点进去才知道原来是一个创建MessageQueue的工厂类,同样这里也是根据一些配置去加载不同的MessageQueue实现

  • MailboxType is a factory to create MessageQueues for an optionally
终于到了最后一步
  • LocalActorRef中会先创建一个actorCell然后再对其进行初始化
private val actorCell: ActorCell = newActorCell(_system, this, _props, _dispatcher, _supervisor)
actorCell.init(sendSupervise = true, _mailboxType)

-在actorCell中才会真正创建邮箱并将actor绑定到他上面,并且在初始化之后会发送一些对应的系统信息

RepointableActorRef 的创建有所不同,因为是异步的,所以这里有调用到了Unsafe类去异步创建cell,并且他里面会有一个underlying和一个lookup的cell,具体原因可以参考如下的官方注释,笔者现在理解还不是很透彻不敢贸然解释

  /*
   * There are two main functions of a Cell: message queueing and child lookup.
   * When switching out the UnstartedCell for its real replacement, the former
   * must be switched after all messages have been drained from the temporary
   * queue into the real mailbox, while the latter must be switched before
   * processing the very first message (i.e. before Cell.start()). Hence there
   * are two refs here, one for each function, and they are switched just so.
   */
  def underlying: Cell = Unsafe.instance.getObjectVolatile(this, cellOffset).asInstanceOf[Cell]
  def lookup = Unsafe.instance.getObjectVolatile(this, lookupOffset).asInstanceOf[Cell]

你可能感兴趣的:(akka,scala)