So I have 2 actors, ClientIn and Maker.
I need to have a reference of Maker inside of ClientIn, so I can forward some messages that ClientIn recieves to Maker.
I added Maker to the receptionist, but since clientIn is created first, and maker 2nd I'm not sure how to query to fetch the actorRef.
Actually I'm just confused how I can now query the receptionist so that clientIn will get the reference to the Maker actor.
Here is what I have:
// guardian actor constructor:
Behaviors.setup[Any] { context =>
implicit val ec: ExecutionContext = context.executionContext
val system = context.system
val clientIn = context.spawn(ClientIn(), "ClientIn")
val maker = context.spawn(Maker(clientOut), "mmaker")
....
}
ClientIn.scala:
object ClientIn {
sealed trait ClientInCommand
case class Watch(symbol: String) extends ClientInCommand
case class Command(value: String) extends ClientInCommand
private case class ListingResponse(listing: Receptionist.Listing)
extends ClientInCommand
def apply(): Behavior[ClientInCommand] = {
Behaviors.setup { context =>
context.log.info(s"ClientIn starting...${context.self}")
val listingResponseAdapter =
context.messageAdapter[Receptionist.Listing](ListingResponse.apply)
var maker: Option[ActorRef[Maker.MMCommand]] = None
Behaviors.receiveMessage[ClientInCommand] {
case Watch(symbol) =>
context.log.info(s"ClientIn watch message received..$symbol")
Behaviors.same
case Command(value) =>
context.log.info(s"ClientInCommand.Command with value:$value")
// TODO: Maker ! RecieveCommand(value)
Behaviors.same
case ListingResponse(Maker.makerKey.Listing(listings)) =>
maker = Some(listings.head)
Behaviors.same
}
}
}
}
Maker.scala:
object Maker {
val makerKey = ServiceKey[MMCommand]("makerKey")
sealed trait MMCommand
case object Startup extends MMCommand
case object Shutdown extends MMCommand
def apply(): Behavior[MMCommand] = {
Behaviors.setup { context =>
context.system.receptionist ! Receptionist.Register(
makerKey,
context.self
)
Behaviors.receiveMessage[MMCommand] {
case Startup =>
context.log.info("Maker startup message received..")
Behaviors.same
case Shutdown =>
context.log.info("Maker shutdown message received..")
Behaviors.same
}
}
}
}
CodePudding user response:
You can send a Receptionist.Subscribe(Maker.makerKey) message to the receptionist, as described in the Akka documentation on Actor discovery:
context.system.receptionist ! Receptionist.Subscribe(Maker.makerKey, listingResponseAdapter)
Quoting the documentation:
It will send Listing messages to the subscriber, first with the set of entries upon subscription, then whenever the entries for a key are changed.
This allows it to receive the maker ActorRef even if it is registered with the receptionist after the ClientIn actor is spawned. However, it is possible in principle that ClientIn may receive other messages such as Command before this happens. It will need to handle these cases, perhaps by stashing or dropping commands until the Maker has been registered.
In this specific example, it would be simpler to avoid this requirement by spawning the Maker first, and passing its ActorRef to ClientIn.apply:
val maker = context.spawn(Maker(), "mmaker")
val clientIn = context.spawn(ClientIn(maker), "ClientIn")
Then, in ClientIn:
def apply(maker: ActorRef[Maker.MMCommand]): Behavior[ClientInCommand] = {
Behaviors.setup { context =>
context.log.info(s"ClientIn starting...${context.self}")
Behaviors.receiveMessage[ClientInCommand] {
case Watch(symbol) =>
context.log.info(s"ClientIn watch message received..$symbol")
Behaviors.same
case Command(value) =>
context.log.info(s"ClientInCommand.Command with value:$value")
maker ! Maker.ReceiveCommand(value)
Behaviors.same
}
}
}
Note that the ActorRef returned from spawn is able to receive messages immediately, even if the actor hasn't fully initialized.
