更新:可以找到此问题的源代码here。由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
如果没有actorFor,我该怎么做?哦,DI构造器注入在这里不起作用 - 因为飞行员和副驾驶员需要彼此了解。
感谢。
P.S。:以下是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)
}
感谢Derek Wyatt(作者)给出的答案。
例如,以下是我在一个map
actor中使用带有Copilot
表示法的Futures来获取对Pilot
actor的依赖:
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
}
}
答案 0 :(得分:6)
ActorSelection
确实是替换actorFor
的方法。但是,正如我在书中已经说过的那样,使用Actor路径或选择可能会使你的应用变得非常脆弱。
context.actorSelection("../someactor") ! ReadyToGo
context.actorSelection(self / "someChild" / "someGrandchild") ! ReadyToGo
更改您的演员层次结构,您的应用开始失败。但是,至少在ActorSelection
时会出现一些超时错误,而不仅仅是将事情发送到死信办公室。
现在,如果你想开始抓取引用,这是一个不同的故事,我建议用Future
来做。
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
}
现在,您的Actor有一个完全异步的启动,可以正确地获取所有依赖项。重启后,你很高兴。
答案 1 :(得分:2)
我认为你有两种可能性:
通过消息“注入”演员(例如ReadyToGo):
coPilotActor ! ReadyToGo(pilotActor)
pilotActor ! ReadyToGo(copilotActor)
使用ActorSelection:
context.actorSelection(actorPath) ! Identify(<correlation_id>)
// ...
// response:
case ActorIdentity(<correlation_id>, None) => // actor ref not found
// or
case ActorIdentity(<correlation_id>, Some(ref)) => // actor ref found
因为你是在平面上创建两个演员,所以你可能找到一个解决方案,在这种情况下你不必使用演员选择。