Akka源码分析-Remote-Actor创建

  在之前的博客中,我们分析过local模式下Actor的创建过程,最终还是调用了provider的actorOf的函数创建了Actor,在remote模式下provider就是RemoteActorRefProvider,所以这样就知道在哪里最终创建了Actor。

def actorOf(system: ActorSystemImpl, props: Props, supervisor: InternalActorRef, path: ActorPath,
              systemService: Boolean, deploy: Option[Deploy], lookupDeploy: Boolean, async: Boolean): InternalActorRef =
    if (systemService) local.actorOf(system, props, supervisor, path, systemService, deploy, lookupDeploy, async)
    else {

      if (!system.dispatchers.hasDispatcher(props.dispatcher))
        throw new ConfigurationException(s"Dispatcher [${props.dispatcher}] not configured for path $path")

      /*
       * This needs to deal with “mangled” paths, which are created by remote
       * deployment, also in this method. The scheme is the following:
       *
       * Whenever a remote deployment is found, create a path on that remote
       * address below “remote”, including the current system’s identification
       * as “sys@host:port” (typically; it will use whatever the remote
       * transport uses). This means that on a path up an actor tree each node
       * change introduces one layer or “remote/scheme/sys@host:port/” within the URI.
       *
       * Example:
       *
       * akka.tcp://sys@home:1234/remote/akka/sys@remote:6667/remote/akka/sys@other:3333/user/a/b/c
       *
       * means that the logical parent originates from “akka.tcp://sys@other:3333” with
       * one child (may be “a” or “b”) being deployed on “akka.tcp://sys@remote:6667” and
       * finally either “b” or “c” being created on “akka.tcp://sys@home:1234”, where
       * this whole thing actually resides. Thus, the logical path is
       * “/user/a/b/c” and the physical path contains all remote placement
       * information.
       *
       * Deployments are always looked up using the logical path, which is the
       * purpose of the lookupRemotes internal method.
       */

      @scala.annotation.tailrec
      def lookupRemotes(p: Iterable[String]): Option[Deploy] = {
        p.headOption match {
          case None           ⇒ None
          case Some("remote") ⇒ lookupRemotes(p.drop(3))
          case Some("user")   ⇒ deployer.lookup(p.drop(1))
          case Some(_)        ⇒ None
        }
      }

      val elems = path.elements
      val lookup =
        if (lookupDeploy)
          elems.head match {
            case "user" | "system" ⇒ deployer.lookup(elems.drop(1))
            case "remote"          ⇒ lookupRemotes(elems)
            case _                 ⇒ None
          }
        else None

      val deployment = {
        deploy.toList ::: lookup.toList match {
          case Nil ⇒ Nil
          case l   ⇒ List(l reduce ((a, b) ⇒ b withFallback a))
        }
      }

      Iterator(props.deploy) ++ deployment.iterator reduce ((a, b) ⇒ b withFallback a) match {
        case d @ Deploy(_, _, _, RemoteScope(address), _, _) ⇒
          if (hasAddress(address)) {
            local.actorOf(system, props, supervisor, path, false, deployment.headOption, false, async)
          } else if (props.deploy.scope == LocalScope) {
            throw new ConfigurationException(s"configuration requested remote deployment for local-only Props at [$path]")
          } else try {
            try {
              // for consistency we check configuration of dispatcher and mailbox locally
              val dispatcher = system.dispatchers.lookup(props.dispatcher)
              system.mailboxes.getMailboxType(props, dispatcher.configurator.config)
            } catch {
              case NonFatal(e) ⇒ throw new ConfigurationException(
                s"configuration problem while creating [$path] with dispatcher [${props.dispatcher}] and mailbox [${props.mailbox}]", e)
            }
            val localAddress = transport.localAddressForRemote(address)
            val rpath = (RootActorPath(address) / "remote" / localAddress.protocol / localAddress.hostPort / path.elements).
              withUid(path.uid)
            new RemoteActorRef(transport, localAddress, rpath, supervisor, Some(props), Some(d))
          } catch {
            case NonFatal(e) ⇒ throw new IllegalArgumentException(s"remote deployment failed for [$path]", e)
          }

        case _ ⇒
          local.actorOf(system, props, supervisor, path, systemService, deployment.headOption, false, async)
      }
    }

   上面的代码很多,简单起见,先只分析最后一段代码,即使lookupRemotes这个函数也非常重要。最后一段代码的逻辑是根据配置信息判断最终是创建本地actor还是远程actor(也就是RemoteActorRef)。其实这一点还是有点不好理解的,怎么绕来绕去还是用LocalActorRef创建了actor呢?其实关于这一点,我们还需要从官网的文档找到一些细节。官网原文如下:


If you want to use the creation functionality in Akka remoting you have to further amend the application.conf file in the following way (only showing deployment section):

akka {
  actor {
    deployment {
      /sampleActor {
        remote = "akka.tcp://sampleActorSystem@127.0.0.1:2553"
      }
    }
  }
}

The configuration above instructs Akka to react when an actor with path /sampleActor is created, i.e. using system.actorOf(Props(...), "sampleActor"). This specific actor will not be directly instantiated, but instead the remote daemon of the remote system will be asked to create the actor, which in this sample corresponds to sampleActorSystem@127.0.0.1:2553.

Once you have configured the properties above you would do the following in code:


val actor = system.actorOf(Props[SampleActor], "sampleActor") actor ! "Pretty slick"

The actor class SampleActor has to be available to the runtimes using it, i.e. the classloader of the actor systems has to have a JAR containing the class.


 

  这句话的意思大概是,你可以指定某个actor在远程节点创建!感觉真是神一样的操作!仅仅通过配置就能实现!至少我看到这段说明的时候,感到有点不可思议。当然前提是对应的Actor类能够在远程加载到,也就是包含该类的jar在远程被加载过。这就是RemoteActorRef存在的意义。

  分析到这里我们发现,remote模式下,actor的创建基本都转发给了LocalActorRef,那么这样创建的actor能够跨网络传输吗。其实我们仔细思考一下大概就明白了,不管是什么模式创建的actor,一定会有一个本地actor的。毕竟我们发消息的代码都在本地,这就必须有一个本地ActorRef实例。如果要跨网络发消息,就必须拿到远程Actor的ActorRef,而ActorRef我们知道是可以序列化后跨网络传输的,获取到了远程的ActorRef,就可以发远程消息了。至于远程部署Actor,其实也就是在本地创建了一个特殊的Actor(RemoteActorRef、ReliableDeliverySupervisor)作为代理,把消息通过对应的网络对象转发到远程而已。关于这一点我们后面再分析。

  remote模式下,Actor的创建也就基本结束了,我们发现,这个跟local模式下的大概流程差别也不是很大,只是维护了与网路相关的对象。不过正是这些网络对象,使得我们可以给远程actor发消息。

posted @ 2018-07-27 13:40  gabry.wu  阅读(769)  评论(0编辑  收藏  举报