Reputation: 92149
My Actor hierarchy for Monitoring
looks like
Monitor
/ \
Disk Memory
/ \
Remote Local
Where Monitor
asks its children actors to get the data and so on.
In the end Monitor receive all the data and prints it on console (for example).
Considering only Memory
Hierarchy, I realized that in ask
pattern, the call in hierarchy looked like
(at Memory level)
import ExecutionContext.Implicits.global
implicit val timeout = Timeout(5 seconds)
lpMemory <- (lpMemory ? CollectLPMemoryStat).mapTo[MemoryInformation]
(at Remote Level)
implicit val timeout = Timeout(5 seconds)
import context.dispatcher
(remoteActor ? HealthReportRequest).mapTo[MemoryInformation].pipeTo(sender)
So at every level in ask
pattern, I needed to do 4 things
ExecutionContext
Timeout
mapTo[MemeoryInformation]
pipeTo(sender)
This gets cumbersome if there are more hierarchies.
Since I am new to Akka
and Scala
stack, I am looking to ideas where I can make this code much better
Thanks
Upvotes: 1
Views: 113
Reputation: 5049
Here is what I would do, to cut the overhead a bit:
Extract the timeout in a separate trait and mix it in to the actors. All in all, the actors are classes as any other, the OOP principles still stick to it. I would define the import ExecutionContext
and specify the timeout into it.
import akka.util.Timeout
import scala.concurrent.duration._
trait AskEnabled {
implicit val timeout = Timeout(5 seconds)
implicit val ec = scala.concurrent.ExecutionContext.Implicits.global
}
The implicit ec
value is a bit of a hack, since I learned during the writing of this answer that the imports from the traits are actually not propagated. So, this may be slightly disputable, in case one day the framework does not expect ec
but execContext
for example, but in general keeps the code clean.
Anyway, the actor looks like:
class Actor1 extends Actor with AskEnabled {
val actor2 = context.actorOf(Props[Actor2])
override def receive: Receive = {
case "send" => {
val responseFuture = (actor2 ? "request").mapTo[String]
responseFuture.onSuccess {
case response: String => println(response)
}
}
}
}
I believe there is no need to both map and then pipe at the Remote Level, piping it directly should work well. The return type of the ?
is a Future[Any]
, however it is only upcasted to Any
, you don't actually lose the type information when shuffling it around. It's more from Scala's type inference thing rather than Akka. However the upcast is something Akka does, where the whole type safety thing gets blurry, at least until TypedActors become more popular, I suppose.
(remoteActor ? HealthReportRequest).pipeTo(sender)
If you have more a greater hierarchy in which you are reusing some functionality, then use the functional nature of Scala, extract that method (somewhere) and pass it to each actor. Functions are a first class citizen and you can pass them in the constructor, avoiding repeating yourself. This is a rough idea, you can also try mixing it in into a trait
. Take a look at: Scala Passing Function with Argument .
Upvotes: 2