In this tutorial, we will write a program that demonstrates the life cycle of an Akka actor by overriding the following four functions: preStart(), preRestart(), postStop() and postRestart().The use of these four functions can be described using the following chart (Note: preRestart() is called by the old instance while postRestart() is called by the new one. By default, preRestart() dismiss all children of the old instance, then call the postStop() function while the postRestart() function will call preStart()):
First, we define the LifeCycleActor by re-using the code presented on the tutorial Writing a basic Akka program. However, in the default case, we will raise an Exception instead of displaying a warning “Received unknown message!!!” (to illustrate the process of restarting Akka Actor):
1 2 3 4 5 6 7 8 |
//Implement receive method def receive = { //Define how actor handle each message case msg:String => println(self.path.name+" received a message from "+sender().path.name+": "+msg) // By default, an exception will be raised if actor received unknown message case _ => throw new Exception("Received unknown message!!!") } |
To override four functions: preStart(), preRestart(), postStop() and postRestart(), we can do as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//Override preStart() method override def preStart(){ println("preStart method is called"); //Reuse the implementation of Akka super.preStart() } //Override postStop() method override def postStop(){ println("postStop method is called"); //Reuse the implementation of Akka super.postStop() } //Override preRestart() method override def preRestart(reason:Throwable, message: Option[Any]){ println("preRestart method is called. Reason: "+reason); //Reuse the implementation of Akka super.preRestart(reason, message) } //Override postRestart() method override def postRestart(reason:Throwable){ println("postRestart is called. Reason: "+reason); //Reuse the implementation of Akka super.postRestart(reason) } |
To check the operation of the above four functions, we send two messages: one is a normal message (in String format) and the other is an message that raise an Exception (not in String format):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//Creating an ActorSystem var actorSystem = ActorSystem("ActorSystem"); //Create a BasicActor called "TestActor" var actor = actorSystem.actorOf(Props[LifeCycleActor],"LifeCycleActor") //Sending some messages using ! println("Sending normal message: ") actor ! "Hello Akka" Thread.sleep(1000) println("nMaking actor restart: ") actor ! 1010 Thread.sleep(1000) println("nStopping actor") actorSystem.stop(actor) //Terminate the actorSystem actorSystem.terminate() |
Run the code, we get the following result:
In the case when Actor receive a String message, only preStart() function is called. However, if the Exception occurs, all four functions are called with the following order: preRestart is called to delete the old instance and then it calls postStop() function. After the postStop() function is executed, postRestart() an preStart() are in turn called by the newly created instance. Similarly, when we stop the actor, the postStop() function is used to release all resources of that actor.
So, we have finished writing a program that use four functions: preStart(), preRestart(), postStop() and postRestart() to illustrate the life cycle of an Akka actor. The full code of this tutorial is provided below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
import akka.actor.Actor; import akka.actor.ActorSystem; import akka.actor.Props; //Define Actors by extending Actor trait class LifeCycleActor extends Actor{ //Implement receive method def receive = { //Define how actor handle each message case msg:String => println(self.path.name+" received a message from "+sender().path.name+": "+msg) // By default, an exception will be raised if actor received unknown message case _ => throw new Exception("Received unknown message!!!") } //Override preStart() method override def preStart(){ println("preStart method is called"); //Reuse the implementation of Akka super.preStart() } //Override postStop() method override def postStop(){ println("postStop method is called"); //Reuse the implementation of Akka super.postStop() } //Override preRestart() method override def preRestart(reason:Throwable, message: Option[Any]){ println("preRestart method is called. Reason: "+reason); //Reuse the implementation of Akka super.preRestart(reason, message) } //Override postRestart() method override def postRestart(reason:Throwable){ println("postRestart is called. Reason: "+reason); //Reuse the implementation of Akka super.postRestart(reason) } } object ActorLifeCycleEx { def main(args:Array[String]){ //Creating an ActorSystem var actorSystem = ActorSystem("ActorSystem"); //Create a BasicActor called "TestActor" var actor = actorSystem.actorOf(Props[LifeCycleActor],"LifeCycleActor") //Sending some messages using ! println("Sending normal message: ") actor ! "Hello Akka" Thread.sleep(1000) println("nMaking actor restart: ") actor ! 1010 Thread.sleep(1000) println("nStopping actor") actorSystem.stop(actor) //Terminate the actorSystem actorSystem.terminate() } } |