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
CreateChildandNamecase classes are created. They’ll be used to send messages to the actors. - The
Childactor has areceivemethod that can handle aNamemessage. It uses that message to set itsnamefield. - The
receivemethod of theParentactor can handle aCreateChildmessage. When it receives that message, it creates a newChildactor with the given name. Notice that it callscontext.actorOfto do this. - The
ParentChildDemoobject creates a newActorSystem, and then creates theParentactor using theActorSystemreference. It then sends twoCreateChildmessages to theparentactor reference. After a brief pause, it looks up theChildactor namedJonathan, and then sends it aPoisonPillmessage. After another pause, it shuts down the system using theActorSystemreference.
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 |