The Actor life cycle is represented and controlled by Hooks, which can be overridden to achieve fine-grained control over all aspects of the Actor life cycle. The sequence of events is as follows:
Prestart () :
Called after the constructor.PostStop () :
Called before restart.PreRestart (" reason, the message) :
PostStop () is called by default.PostRestart () :
PreStart () is called by default.
Note: preRestart and postRestart are only called on reboot. They call preStart and postStop by default, but they no longer call preStart and postStop directly. This allows us to decide whether to call preStart and postStop only when the Actor starts or stops, or whether to call preStart and postStop every time an Actor is restarted.
From the figure above, we can see that the Actor life cycle consists of three states: start, stop, and restart.
Enabling the Start policy
The Start policy is typically used to initialize the resource and invoke the preStart Hook. When Akka builds an Actor through Props, the constructor is called, followed by a call to preStart.
override def preStart={
log.info ("Starting storage actor...")
initDB
}
Copy the code
Stop Stop policy
The Stop strategy is typically used to recover resources. An Actor can be stopped when an operation is completed, an exception occurs, or an Actor is forced to Stop by sending Kill, PoisonPill, etc. The termination process is divided into two steps:
- The Actor suspends processing of the mailbox, sends termination commands to all the child actors, and then processes termination messages from the child actors until all the child actors have finished terminating.
- Terminate yourself, call the postStop method, empty your mailbox, and publish to DeathWatch
Terminated
And notify its regulators.
The entire human process ensures that subtrees in the Actor system terminate in an orderly fashion, propagating termination commands to leaf nodes and collecting their acknowledgement messages back to the terminating supervisor. If one of the actors does not respond (that is, because it took too long to process the message and did not receive a termination command), the whole process will be blocked.
override def postStop={
log.info ("Stopping storage actor...")
db.release
}
Copy the code
Restarting the Restart policy
The Restart policy is one of the most complex situations, and it can happen for many reasons during the life of an Actor. There are several reasons why an Actor needs to restart:
(1) A system exception occurs during the processing of a particular message, and a restart is required to clean up the system error;
(2) The internal state is destroyed and must be rebuilt by restarting;
(3) Some dependent resources cannot be used during message processing, and a restart is required to reconfigure resources.
The restart process of Actor is also a recursive process. Due to the complexity of the process, the previous figure is:
By default, the restart process is divided into the following steps:
- Actor is suspended;
- Call the old instance supervisionStrategy. HandleSupervisorFailing method (the default implementation for all child actor hang);
- PreRestart stops all children from running preRestart. (Stop action, attention!) And call postStop to reclaim the resource.
- Call the old instance supervisionStrategy. HandleSupervisorRestarted method (the default implementation for all the rest of the a restart of the actor request);
- Wait for all child actors to terminate until preRestart finally ends;
- Call the previously supplied actor factory again to create a new actor instance;
- Call postRestart on the new instance;
- Resume running the new actor;
Default implementation: a constructor that takes no parameters, for example, is a default implementation.
Common problems
What’s the difference between the Restart policy and the Stop policy?
The Stop policy calls postStop(), and the Restart policy also calls postStop(), but the Restart policy does not Stop the old Actor through the Stop policy. UID and Path remain the same. That is, there is no need to retrieve the ActorRef again after being restarted.
Actor is uniquely identified by UID and Path, which means ActorRef is also located by UID and Path. After the Actor is stopped, the new Actor can use this Path, but the old ActorRef cannot be used because the UID is different.
What’s so special about preRestart Hook?
The default preRestart Hook stops all Children with a Stop policy, and Children are started with a Stop ->Start policy rather than being recursively restarted. If an external Actor holds the old Chidren ActorRef, the Ref will not work because the Path is correct, but the UID has changed!
What’s so special about postRestart Hook?
The default postRestart is to call preStart(), so that both the constructor and the preStart method are called again during restart. If a resource wants to be initialized only once, the method must be overridden. So typically creating children is in preStart.
override def preStart() :Unit = {
// Initialize children
}
// Overwrite postRestart to prevent preStart from being called every restart
override def postRestart(reason: Throwable) :Unit = ()
override def preRestart(reason: Throwable, message: Option[Any) :Unit = {
// Still clean up, but do not Stop children
postStop()
}
Copy the code
The Actor lifecycle tests the code
Parentactor. scala: Receives messages sent from main. scala and initializes or controls ChildActor.
package com.lp.akka.notes.lifecycle
import akka.actor.{Actor.ActorLogging.Props.ReceiveTimeout}
/** * @author li.pan * @version 1.0.0 * @description Parent Actor * @createTime 2021 01月13日 13:04:00 */
class ParentActor extends Actor with ActorLogging {
println("start pActor ")
def receive = {
case "test" => log.info("received test")
case ("newChild"The name:String) => context.actorOf(Props[ChildActor], name)
case ("stop"The name:String) = > {val child = context.actorFor(self.path + "/" + name);
context.stop(child)
}
case ReceiveTimeout= >throw new RuntimeException("received timeout"); // Throw an exception every timeout when no message is received
case "suicide"= >case x: Any => log.info("received unknown message :" + x)
}
/** * is executed after the actor is instantiated. */ is not executed on reboot
override def preStart {
println("actor:" + self.path + ", parent preStart")}/** * execute when actor terminates normally, not when actor restarts abnormally. * /
override def postStop {
println("actor:" + self.path + ",parent postStop .")}/** * Save the state before the actor restarts abnormally */
override def preRestart(reason: ThrowableThe message:Option[Any]) {
println("actor:" + self.path + ", preRestart parent, reason:" + reason + "The message," + message)
}
/** * Restores the state of the actor after an abnormal restart */
override def postRestart(reason: Throwable) {
println("actor:" + self.path + ", postRestart parent, reason:" + reason)
}
}
Copy the code
Scala: ChildActor, controlled by ParentActor.
package com.lp.akka.notes.lifecycle
import akka.actor.Actor
/** * @author li.pan * @version 1.0.0 * @description subactor * @createTime 2021 01月13 13:03:00 */
class ChildActor extends Actor {
override def receive() = {
case "abc" => println("get abc string ")
case "exception"= >throw new NullPointerException(a)case _ => println("children cann't handle unknown message")}override def preStart {
println("actor:" + self.path + ",child preStart .")}override def postStop {
println("actor:" + self.path + ",child postStop .")}override def preRestart(reason: ThrowableThe message:Option[Any]) {
println("actor:" + self.path + ", preRestart child, reason:" + reason + "The message," + message)
}
override def postRestart(reason: Throwable) {
println("actor:" + self.path + , postRestart child, reason:" + reason)
}
}
Copy the code
LifeCycleMainApp: Initializes and sends messages to actors.
package com.lp.akka.notes.lifecycle
import akka.actor.{ActorSystem.Kill.Props}
/** * @author li.pan * @version 1.0.0 * @description primary class * @createTime 2021 01月13 13:05:00 */
object LifeCycleMainApp extends App {
// Build the Actor system
val system = ActorSystem("lpLocalSys")
// Create the parent Actor using Props
val pActor = system.actorOf(Props[ParentActorName =]"pActor")
pActor ! ("newChild"."child-1")
pActor ! ("newChild"."child-2")
pActor ! "test"
val parent = system.actorSelection("akka://lpLocalSys/user/pActor")
parent ! "test"
// parent ! (" stop ", "the child - 2")
val child2 = system.actorSelection("akka://lpLocalSys/user/pActor/child-2")
child2 ! Kill / / kill child2
// child2 ! "exception"
Thread.sleep(5000) // Wait for Child2 to be killed
pActor ! ("newChild"."child-2")
// Thread.sleep(5000)
// myActor ! (" newChild ", "the child - 2")
}
Copy the code
Running results:
In LifeCycleMainApp, creating an actor with the same name if the child-2 is not killed will cause an exception to the parent actor named MyActor, causing it to restart, which will also restart its child actor, according to Akka Actor’s regulatory policy. So child-1 and child-2 are also restarted, with the following output
As you can see from the output, the parent Actor first calls preRestart, then is instantiated, then calls postRestart, and finally restarts its child actors, which follow the same steps.
reference
- Introduction and Practice of Akka
- Coderbee.net/index.php/a…
- www.jianshu.com/p/16de393ec…
- www.codetd.com/article/466…
- www.cnblogs.com/junjiang3/p…
Pay attention to the public number, focus on Java big data field offline, real-time technology dry goods regular sharing! Reply to Akka to get the book Akka Introduction and Practice, personal website www.lllpan.top