This is an excerpt from the Scala Cookbook (partially modified for the internet). This is a short recipe, Recipe 13.5, “How to start an Akka Actor in Scala.”
Problem
You want to start an Akka actor in a Scala application, or attempt to control the start of an actor.
Solution
This is a bit of a tricky problem, because Akka actors are started asynchronously when they’re passed into the actorOf
method using a Props
. At the ActorSystem
level of your application, you create actors by calling the system.actorOf
method. Within an actor, you create a child actor by calling the context.actorOf
method.
As demonstrated in Recipe 13.1, you can create an actor at the ActorSystem
level by passing your actor class name (such as HelloActor
) to the system.actorOf
method, using the Props
case class:
val system = ActorSystem("HelloSystem") // the actor is created and started here val helloActor = system.actorOf(Props[HelloActor], name = "helloactor") helloActor ! "hello"
The process of creating a child actor from within another actor is almost identical. The only difference is that you call the actorOf
method on the context
object instead of on an ActorSystem
instance. The context
object is implicitly available to your actor instance:
class Parent extends Actor { val child = context.actorOf(Props[Child], name = "Child") // more code here ... }
Discussion
The following complete example demonstrates how to create actors both at the system level and from within another actor:
package actortests.parentchild import akka.actor._ case class CreateChild (name: String) case class Name (name: String) class Child extends Actor { var name = "No name" override def postStop { println(s"D'oh! They killed me ($name): ${self.path}") } def receive = { case Name(name) => this.name = name case _ => println(s"Child $name got message") } } class Parent extends Actor { def receive = { case CreateChild(name) => // Parent creates a new Child here println(s"Parent about to create Child ($name) ...") val child = context.actorOf(Props[Child], name = s"$name") child ! Name(name) case _ => println(s"Parent got some other message.") } } object ParentChildDemo extends App { val actorSystem = ActorSystem("ParentChildTest") val parent = actorSystem.actorOf(Props[Parent], name = "Parent") // send messages to Parent to create to child actors parent ! CreateChild("Jonathan") parent ! CreateChild("Jordan") Thread.sleep(500) // lookup Jonathan, then kill it println("Sending Jonathan a PoisonPill ...") val jonathan = actorSystem.actorSelection("/user/Parent/Jonathan") jonathan ! PoisonPill println("jonathan was killed") Thread.sleep(5000) actorSystem.shutdown }
Here’s a brief description of that code:
- At the beginning of the code, the
CreateChild
andName
case classes are created. They’ll be used to send messages to the actors. - The
Child
actor has areceive
method that can handle aName
message. It uses that message to set itsname
field. - The
receive
method of theParent
actor can handle aCreateChild
message. When it receives that message, it creates a newChild
actor with the given name. Notice that it callscontext.actorOf
to do this. - The
ParentChildDemo
object creates a newActorSystem
, and then creates theParent
actor using theActorSystem
reference. It then sends twoCreateChild
messages to theparent
actor reference. After a brief pause, it looks up theChild
actor namedJonathan
, and then sends it aPoisonPill
message. After another pause, it shuts down the system using theActorSystem
reference.
Although it isn’t required, in this case, the child
actor instance is created in the constructor of the Parent
actor. The Child
actor could have been created when the Parent
actor received a message, so in a sense, that gives you a way to control when an actor instance is created.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |