Reputation: 2448
I am pretty new to akka actor system, and was wandering what are the best practices for executing common logic in an actor. So here is an example:
I have the following actor:
class MyActor @Inject()(eventBus: EventBus) extends Actor{
eventBus.subscribe(context.self, Topics.TimeoffPolicy)
override def receive: Receive = {
case Foo(name:String) => {
//Need to execute foo related logic, and then Bar related logic
}
case Bar(name:String) => {
//Need to execute bar related logic
}
case a: Any => log.warning(f"unknown message actor ${a.toString}")
}
}
So one option is to extract common logic into a method and call it when handling Foo like this:
class MyActor @Inject()(eventBus: EventBus) extends Actor{
eventBus.subscribe(context.self, Topics.TimeoffPolicy)
override def receive: Receive = {
case Foo(name:String) => {
foo(name)
bar(name)
}
case Bar(name:String) => {
bar(name)
}
case a: Any => log.warning(f"unknown message actor ${a.toString}")
}
}
Other option is to send message to myself:
class MyActor @Inject()(eventBus: EventBus) extends Actor{
eventBus.subscribe(context.self, Topics.TimeoffPolicy)
override def receive: Receive = {
case Foo(name:String) => {
foo()
self ! Bar(name)
}
case Bar(name:String) => {
bar(name)
}
case a: Any => log.warning(f"unknown message actor ${a.toString}")
}
}
Here, it make sense to send a message, and keep the logic encapsulated in every message handling, but I guess it is less error prune to extract the common logic to a method, and invoke it. what is recommended?
Same goes in case of a common logic between actors, one option is to send message over the event bus to the other actor to invoke, so actor1 will execute foo, and the other one will execute bar. The second option is to extract the same logic into another class, and inject that class to both actors, so no in order to execute foo and bar, there will be no communication between the actors.
What do you think?
Upvotes: 0
Views: 370
Reputation: 2900
Both approaches seem legit to me, I'd choose one of the two depending on whether I have calls to other actors inside the shared logic or not. Basically, synchronous logic can perfectly be reused by extracting a method, and asynchronous logic will require passing a message back to self.
Messages to self
are also definitely preferred to be sent from Future
callbacks (actually, it is the only right way to do in order not to mess with order of execution of the actor's tasks) and in scheduled activities.
Here's a snippet to illustrate synchronous approach:
class MyActor extends Actor with ActorLogging {
def receive = {
case Foo(foo) =>
doSomeFooSpecificWork()
logFooOrBar()
sender() ! "foo done"
case Bar =>
logFooOrBar()
sender() ! "bar done"
}
def logFooOrBar() = log.debug("A common message is handled")
}
And here's what I'd write for asynchronous one:
import akka.pattern.{ask, pipe}
class MyActor extends Actor {
val logger = context.actorOf(Props[DedicatedLogger])
def receive = {
case Foo(foo) =>
doSomeFooSpecificWork()
loggerActor ? LogFooOrBar(sender(), foo) pipeTo self
case Bar(bar) =>
loggerActor ? LogFooOrBar(sender(), bar) pipeTo self
case LoggedFoo(reportTo, foo) => reportTo ! "foo done"
case LoggedBar(reportTo, bar) => reportTo ! "bar done"
}
}
Upvotes: 1