Akka Notes - Child Actors and ActorPath - 6
Actors are completely hierarchical. Whatever Actors that you create HAS to be a child of some other Actor.
Let's analyze that a bit :
Path
Say, we create an ActorRef using ActorSystem.actorOf
and try to print its path
.
val actorSystem=ActorSystem("SupervisionActorSystem")
val actorRef=actorSystem.actorOf(Props[BasicLifecycleLoggingTeacherActor])
println (actorRef.path) // (prints) akka://SupervisionActorSystem/user/$a
As you see, a path looks very similar to a file path in a file system.
akka
here is fixed because all these are addresses of Akka Actors - more likefile://
orhttp://
prefix (nothing to do with protocol though).- The
SupervisionActorSystem
is just the name of your ActorSystem that you created. - We'll talk about the
user
in the next section. - The
$a
is the name of your Actor that the system generated for you. How would you like if your operating system generated random file names for your files? You'd obviously hate it because you would want to refer to that name in future. So, let's give it a proper meaningful name to it :
val actorRef=actorSystem.actorOf(Props[BasicLifecycleLoggingTeacherActor], "teacherActor")
println (actorRef.path) // (prints) akka://SupervisionActorSystem/user/teacherActor
That's it. Now, the path actually makes sense.
Child Actors
Similar to top level actors which we create out of ActorSystem, we could also create child actors out of the ActorContext
. In fact, the power of Actors's fault tolerance primarily lie within leveraging the Actor hierarchy and the ability of a parent to manage the life of child actors.
Assume you have a TeacherSupervisor
and you would like to create a TeacherActor
to be a child of the Supervisor, you do a ActorContext.actorOf
instead of a ActorSystem.actorOf
:
class TeacherSupervisor extends Actor with ActorLogging {
val teacherActor=context.actorOf(Props[TeacherActor], "teacherActor")
...
...
Frankly, in any application, you will be creating a whole lot of child actors than top level actors - which means that you'll be calling a lot more actorContext.actorOf
than actorSystem.actorOf
.
You'll notice that the path of the child actor is akka://SupervisionActorSystem/user/teacherSupervisor/teacherActor
, which is very similar to the path you get when you create a child folder within a parent folder.
When do you create child Actors?
You generally create child actors when a particular task is composed of a subtask or multiple subtasks. You also create a child actor when a particular task to be executed by the parent is error-prone and you would want to isolate it (so that if it fails, you could recover). When there is no parent child relationship between tasks, then you DON'T create child actors.
Also, there's nothing which is stopping a child actor from creating children to delegate its subtasks. Actors and their creation are really cheap but the power that comes with it is amazing (we'll see about this while we talk about supervision).
Now what is that user
in the path?
For lack of a creativity, let me compare the ActorSystem
to a Unix file system - with a /
root folder and all those /etc
, /usr
, /bin
and various other folders.
ActorSystem are much like that. It creates a few top level Actors - the most important being the root Actor with the path /
, the user Actor with the path /user
and a system Actor with the path /system
. (there's also a /deadLetters
that represent the DeadLetterActorRef
. We saw this in our previous post)
Codewise, the ActorSystem composes three Actors inside it (via ActorRefProvider). Those are the root for ALL the Actors that gets created under the ActorSystem.
systemGuardian
actor - root of all actors under/system
guardian
actor - root of all actors under/user
androotGuardian
Actor - root of both thesystemGuardian
and theuserGuardian
actors.
/**
* Reference to the supervisor of guardian and systemGuardian; ....
*/
def rootGuardian: InternalActorRef
/**
* Reference to the supervisor used for all top-level user actors.
*/
def guardian: LocalActorRef
/**
* Reference to the supervisor used for all top-level system actors.
*/
def systemGuardian: LocalActorRef
/user (aka) user guardian
Any Actor that you create in your program like the StudentActor
or the TeacherActor
using the ActorSystem
's actorOf
method would directly fall under /user
. That's the reason your teacherActor
in the first part of this write-up had the path slug /user/teacherActor
.
/system (aka) system guardian
The system guardian shuts itself down when it notices that the userGuardian
is dead. Makes sense considering if the userGuardian
is down, then all the business actors under it is also down and therefore all administrative actors needs to be gone too.
We could see two distinct places where System Actors are being created - I mean actors under the /system
hierarchy.
-
Like we saw earlier, any message that you send to an Actor that is terminated gets forwarded to the mailbox of an internal Actor called
DeadLetterActor
. The DeadLetter Actor wraps each message as aDeadLetter
and publishes it to the EventStream. One other Actor calledDeadLetterListener
consumes all DeadLetters and publishes that as a log message. Now, the DeadLetterListener is a system Actor with path/system/deadLetterListener
. -
Remember the
TestEventListener
that we created in our previous write-up to subscribe to log messages in the EventStream? They are System actors too. In fact, allakka.loggers
are created as System actors.
class TeacherTest extends TestKit(ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""")))
...
...
The documentation here says that any Actor that is configured in the configuration files and created and deployed into the ActorSystem while it starts, falls under the /system
umbrella. Let me update this post when I find something interesting around this
/ (aka) root guardian
As we saw earlier, the /
Actor is the parent of the user and the system guardians.
TRIVIA
Technically, there's a superfluous parent for the root actor too. This Actor's only job is to shut down the entire ActorSystem if the root actor fails. Since it's strictly not considered within the Actor hierarchy, the Akka team calls it :
private[akka] val theOneWhoWalksTheBubblesOfSpaceTime: InternalActorRef = new MinimalActorRef {
...