문제

Temporary save sender to reply later

I have a 3 actors

class Actor1 extends Actor {
  val actor2 = context actorOf (Props[Actor2])
  actor2 ! Actor2Request

  def receive = {
    case data: String => // need to receive it
  }
}

class Actor2 extends Actor {

  def receive = {
    case Actor2Request =>
      //sender is Actor1 but I want to save it now and reply it later on in "case DoneActor3(data: String)"
      val actor3 = context actorOf (Props[Actor3])
      actor3 ! Actor3Request

    case DoneActor3(data: String) =>
      // doing something with data
      // and then send it to Actor1
      sender ! data //ops! sender is Actor3 now, not Actor1!
  }
}

class Actor3 extends Actor {

  def receive = {
    case Actor3Request =>
      //doing some work...
      sender ! DoneActor3(data) // sender is Actor2 and that's ok! 
  }
}

Take a look at Actor2 code, I want to save sender (Actor1) when it receives Actor2Request message and reply to this sender later when Actor2 receives DoneActor3(data: String) message.

Is there an idiomatic way to do this?

도움이 되었습니까?

해결책

Use forward, which preserves the original sender when passing messages between actors. In this case, you don't even need to send the return data via Actor2

class Actor2 extends Actor {
  def receive = {
    case Actor2Request =>
      val actor3 = context actorOf (Props[Actor3])
      //sender will still be actor1 when actor3 receives this forwarded message
      actor3 forward Actor3Request
  }
}

class Actor3 extends Actor {
  def receive = {
    case Actor3Request =>
      //doing some work...
      sender ! data // sender is actor1
  }
}

If you do need to return via Actor2, perhaps for some intermediate processing, then you want to capture the original sender and pass it via the actor messages:

class Actor2 extends Actor {
  def receive = {
    case Actor2Request =>
      val actor3 = context actorOf (Props[Actor3])
      //sender at this point is actor1
      actor3 ! Actor3Request(sender)

    case DoneActor3(origin, data) =>
      // Do some other stuff here
      origin ! data //origin is the original sender as passed to Actor3Request
  }
}

class Actor3 extends Actor {
  def receive = {
    case Actor3Request(origin) =>
      //doing some work...
      sender ! DoneActor3(origin, data) // origin is actor1, sender is actor2
  }
}

Or you can mix & match the techniques, having Actor3 send a message direct to origin

UPDATE

There's one final solution, which is valid here because actor2 was created by actor1. In this case we know that actor1 will be the parent of actor2 in the supervision hierarchy:

class Actor2 extends Actor {
  def receive = {
    case Actor2Request =>
      val actor3 = context actorOf (Props[Actor3])
      actor3 ! Actor3Request

    case DoneActor3(data) =>
      // Do some other stuff here
      context.parent ! data //context.parent is actor1
  }
}

class Actor3 extends Actor {
  def receive = {
    case Actor3Request =>
      //doing some work...
      sender ! DoneActor3(data) //sender is actor2
  }
}

다른 팁

Just save the ActorRef in a var and use it later:

class Actor2 extends Actor {

  private var requester: ActorRef = _

  def receive = {
    case Actor2Request =>
      requester = sender
      val actor3 = context actorOf (Props[Actor3])
      actor3 ! Actor3Request

    case DoneActor3(data: String) =>
      requester ! data 
  }
}

That's what actors are for, keeping track of changing states. I know Scala discourages the usage of var, but in the context of actors it's perfectly fine.

Edit for comments: your Actor2 is created in Actor1, there is a one-to-one association between them, so even if you have several instances of Actor1 they'll each have their own child Actor2. I don't know what your goal is with this design, another approach would be to user parent instead of sender in Actor2, that would point to the Actor1 that created it, or you could pass the ActorRef of Actor1 as an argument of the constructor of Actor2.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top