你如何取代actorFor?

And*_*yuk 7 scala akka

更新:此问题的源代码可以在这里找到.由danluu提供.

现在是否有一种标准的替换方法actorFor已被弃用?我找到的所有内容都涉及到actorSelection- 但是通过选择设置获得3个依赖关系是一个真正的皮塔饼.大多数在线讨论只是说不使用actorFor,但akka上的所有书籍仍然使用它(因为它们的目标是2.2之前的akka​​).

例如,假设我有两个演员,Pilot并且Copilot都是Plane演员的孩子.以下是他们目前如何相互参考(Akka Concurrency,Derek Wyatt,2013):

// Plane.scala
...
override def preStart(){
    // Get our children going.

    //... create pilot/copilot (in a separate function)
    startPeople()

    // Tell them system is ready to go
    context.actorFor("Pilots/" + pilotName) ! ReadyToGo
    context.actorFor("Pilots/" + copilotName) ! ReadyToGo

// Pilot.scala
def receive = {
    case ReadyToGo =>
      val copilotName = context.system.settings.config.getString("zzz.akka.avionics.flightcrew.copilotName")
      copilot = context.actorFor("../" + copilotName)

// Copilot.scala
def receive = {
    case ReadyToGo =>
      pilot = context.actorFor("../" + pilotName)
      // watch out for pilot dying - take over control when he does
      context.watch(pilot)

    case Controls(ctr) =>
      controls = ctr

    case Terminated(_) =>
      // Pilot died!
      plane ! GiveMeControl
Run Code Online (Sandbox Code Playgroud)

没有actorFor,我该怎么做?哦,DI构造器注入在这里不起作用 - 因为飞行员和副驾驶员需要彼此了解.

谢谢.

PS:这是startPeople()方法的内容,不确定是否重要:

 def startPeople() {
    val plane = self

    val controls: ActorRef = actorForControls("ControlSurfaces")
    val autopilot: ActorRef = actorForControls("Autopilot")
    val altimeter: ActorRef = actorForControls("Altimeter")

    val people = context.actorOf(Props(new IsolatedStopSupervisor with OneForOneStrategyFactory
    {
      override def childStarter() {
        // These children get implicitly added to the hierarchy
        context.actorOf(Props(newCopilot(plane, autopilot, altimeter)), copilotName)
        context.actorOf(Props(newPilot(plane, autopilot, controls, altimeter)),  pilotName)
      }
    }), "Pilots")

    // Use the default strategy here, which restarts indefinitely
    context.actorOf(Props(newLeadFlightAttendant), attendantName)
    Await.result(people ? WaitForStart, 1.second)
  }
Run Code Online (Sandbox Code Playgroud)

更新:解决了

感谢Derek Wyatt(作者)给出的答案.作为一个例子,这里是我如何使用带有map符号的Futures ,在一个Copilotactor中获取对Pilotactor 的依赖:

package zzz.akka.avionics

import akka.actor._
import zzz.akka.avionics.Pilots.ReadyToGo
import zzz.akka.avionics.Plane.GiveMeControl
import akka.actor.Terminated
import zzz.akka.avionics.Plane.Controls
import scala.concurrent.Future
import akka.util.Timeout
import scala.concurrent.duration._
import akka.pattern.pipe

/** Copilot is a fail-over for the pilot.
 * Created by Andriy Drozdyuk on 05-Apr-14.
 */
class Copilot(plane: ActorRef, autopilot: ActorRef, altimeter: ActorRef) extends Actor with Stash {
  // Implicit execution context for futures
  implicit val ec = context.dispatcher
  // Implicit timeout for getting dependencies
  implicit val timeout = Timeout(1.second)

  val conf = context.system.settings.config
  var pilotName = conf.getString("zzz.akka.avionics.flightCrew.pilotName")

  var controls: ActorRef = context.system.deadLetters

  // Helps us get pilot dependency
  trait PilotAcquisition
  case class PilotAcquired(pilot: ActorRef) extends PilotAcquisition
  case class PilotNotAcquired(t: Throwable) extends PilotAcquisition

  // Acquire the pilot
  // Send either PilotAcquired or PilotNotAcquired message to self
  acquirePilot pipeTo self

  def acquirePilot: Future[PilotAcquisition] = {
    context.actorSelection("../" + pilotName).resolveOne() map {
      pilot => PilotAcquired(pilot)
    } recover {
      case t:Throwable => PilotNotAcquired(t)
    }
  }
  def receive: Receive = waitingForDependencies

  def waitingForDependencies: Receive = {
    case PilotAcquired(pilot) =>
      // Get all the messages we stashed and receive them
      unstashAll()
      // pass all our acquired dependencies in
      context.become(operational(pilot))

    case PilotNotAcquired(t) => throw new IllegalStateException(s"Failed to instantiate: $t")

    // Any other message save for later
    case _ => stash()
  }

  // All our dependencies have been acquired
  def operational(pilot: ActorRef) : Receive = {
    case ReadyToGo =>
      // Start watch on the pilot in case he kicks it
      context.watch(pilot)

    case Controls(ctr) =>
      controls = ctr

    case Terminated(_) =>
      // Pilot died!
      plane ! GiveMeControl

  }


}
Run Code Online (Sandbox Code Playgroud)

Der*_*att 6

ActorSelection真的是取代的方式actorFor.但是,正如我在书中已经说过的那样,使用Actor路径或选择可以使你的应用程序非常脆弱.

context.actorSelection("../someactor") ! ReadyToGo
context.actorSelection(self / "someChild" / "someGrandchild") ! ReadyToGo
Run Code Online (Sandbox Code Playgroud)

更改您的actor层次结构,您的应用程序开始失败.但是,至少ActorSelection你得到一些超时错误,而不是只是把事情发送到死信办公室.

现在,如果你想开始抓取引用,这是一个不同的故事,我建议用Futures 做.

import akka.actor._
import akka.pattern.pipe

class MyActor extends Actor with Stash {
  val actors = for {
    actorA <- ActorSelection(someRootActor / List("child1", "grandchild", "greatGrandchild")).resolveOne()
    actorB <- ActorSelection(someRootActor / List("child2")).resolveOne()
    actorC <- ActorSelection(someOtherRootActor / List("childC")).resolveOne()
  } yield ActorsLocated(actorA, actorB, actorC)
  actors recover {
    case t: Throwable =>
      ActorLocatingFailed(t)
  } pipeTo self

  val uninitialized: Receive = {
    case ActorsLocated(a, b, c) =>
      unstashAll()
      context.become(initialized(a, b, c))
    case ActorLocatingFailed(reason) =>
      throw new IllegalStateException(s"Failed to initialize: $reason")
    case _ =>
      stash()
  }

  def initalized(a: ActorRef, b: ActorRef, c: ActorRef): Receive = {
    // do your stuff here
  }

  def receive = uninitialized
}
Run Code Online (Sandbox Code Playgroud)

现在,您的Actor有一个完全异步的启动,可以正确地获取所有依赖项.重启后,你很高兴.