I. BACKGROUND
Recently, in the development of a project, each module of the project uses Akka GRPC to transmit audio frames, and the actors in each module are persist respectively. This week in the development process encountered a bug, that is, the audio frame after a period of time, the entire system is stuck in a state of death, no response. At first the suspicion is Akka grpc communication, the stream is interrupted, or not transmitted over, but through the capture packet and log, found that each frame of the stream has been accepted. Finally, when the problem is persist, you can find persit failure between each card. We go to see the source code of the persist method can find the above annotations are as follows:
/**
* Asynchronously persists ' event '. On successful persistence, ' handler ' are called with the
* Persisted event. It is guaranteed that no new commands would be received by a persistent actor
* Between a call to ' persist ' and the execution of its ' handler '. This also holds for
* multiple ' persist ' calls per received command. Internally, this was achieved by stashing new
* Commands and unstashing them when the ' event ' has been persisted and handled. The stash used
* For a internal stash which doesn ' t interfere with the inherited user stash.
*
* An event ' handler ' could close over persistent actor state and modify it. The ' sender ' of a persisted
* Event is the sender of the corresponding command. This means, one can reply to a command
* Sender within an event ' handler '.
*
* Within an event handler, applications usually update persistent actor state using persisted event
* data, notify listeners and reply to command senders.
*
* If Persistence of an event fails, [[#onPersistFailure]] 'll be invoked and the actor would
* Unconditionally be stopped. The reason that it cannot resume when persist fails was that it
* is unknown if the event were actually persisted or not, and therefore it's in an inconsistent
* State. Restarting on persistent failures would most likely fail anyway, since the journal
* is probably unavailable. It is better to stop the actor and after a Back-off timeout start
* It again.
*
* @param event event to be persisted
* @param handler handler for each persisted ' event '
*/
def Persist[a] (event:a) (handler:a?) Unit): unit = {
Internalpersist (event) (handler)
}
From the annotations we can see that when we use the persist feature of the Akka, if persistence fails, the corresponding actor will be stop off. Therefore, if you continue to send messages to it, there is no response. And this note suggests that we use Backoff to restart the actor. (It is recommended that we persist the actor to a local or use a plug-in such as Redis to persist to the database, it is best to choose the persistence method, otherwise it will use Java persistence, will appear warn, because the default Java persistence efficiency is not very good). So let's learn the way this Backoff restarts.
Second, normal monitoring and restart
We all know that there is a father-son relationship between actors, if the child actor has an exception, it will be escalated, and the use of policies to deal with the corresponding, the most common strategy is oneforonestrategy, that is, only for the exception of the actor application strategy, Policies define how the various exceptions that occur at subordinate children are handled. The default approach is this:
Final Val Defaultdecider:decider = {
Case _: Actorinitializationexception? Stop
Case _: Actorkilledexception? Stop
Case _: Deathpactexception? Stop
Case _: Exception? Restart
}
However, we can add some of our own special handling methods, such as
Override Val Supervisorstrategy =
Oneforonestrategy (maxnrofretries = ten, Withintimerange = 1 minute) {
Case _: ArithmeticException = Resume
Case _: myexception = Restart
Case T =
Super.supervisorStrategy.decider.applyOrElse (T, (_: any) = escalate)
}
Third, Backoffsupervisor
However, if our actor is launched through System.actorof, it is difficult to customize our own exception handling in this way. And, if we want to have a fine-grained control, for example, how often to deal with an actor after an exception occurs. In this case, we can use backoffsupervisor to achieve.
We can understand backoffsupervisor this way. In fact, Backoffsupervisor is different from the actor who defines Supervisorstrategy. We should think of Backoffsupervisor as an integrated actor. Of course, it is implemented in a way that consists of a pair of father and son actors. The regulatory Strategy (SUPERVISORSTRATEGY) is implemented within the backoffsupervisor. Backoffsupervisor from the outside is like an actor, the arithmetic logic is defined in the child actor, the so-called parent actor has no function other than regulation, we don't even have a place to define the function of the parent actor, Its only function is to forward the received information to the child, which is embedded in the backoffsupervisor. So while we're sending a message to backoffsupervisor, it's actually communicating with its children. Let's take a look at how to use the following example:
Object Innerchild {
Case Class TestMessage (msg:string)
Class Childexception extends Exception
def props = Props[innerchild]
}
Class Innerchild extends Actor with actorlogging {
Import Innerchild._
Override Def receive:receive = {
Case TestMessage (MSG) +//analog sub-function
Log.info (S "Child received message: ${msg}")
}
}
Object Supervisor {
def props:props = {//here defines the regulatory policy and the child actor build
def decider:partialfunction[throwable, supervisorstrategy.directive] = {
Case _: innerchild.childexception = Supervisorstrategy.restart
}
Val options = Backoff.onfailure (Innerchild.props, "Innerchild", 1 second, 5 seconds, 0.0)
. withmanualreset
. Withsupervisorstrategy (
Oneforonestrategy (maxnrofretries = 5, Withintimerange = 5 seconds) (
Decider.orelse (Supervisorstrategy.defaultdecider)
)
)
Backoffsupervisor.props (Options)
}
}
Note: The following is the parent of supervisor, not the parent of Innerchild
Object Parentalactor {
Case Class Sendtosupervisor (Msg:InnerChild.TestMessage)
Case Class Sendtoinnerchild (Msg:InnerChild.TestMessage)
Case Class Sendtochildselection (Msg:InnerChild.TestMessage)
def props = Props[parentalactor]
}
Class Parentalactor extends Actor with actorlogging {
Import Parentalactor._
Build the child actor supervisor here
Val Supervisor = context.actorof (Supervisor.props, "supervisor")
Supervisor! Backoffsupervisor.getcurrentchild//Requires supervisor to return the current child actor
var innerchild:option[actorref] = None//Returns the current child Actorref
Val selectedchild = context.actorselection ("/user/parent/supervisor/innerchild")
Override Def receive:receive = {
Case Backoffsupervisor.currentchild (ref) =//Receive child actor information
Innerchild = ref
Case Sendtosupervisor (msg) = Supervisor! Msg
Case Sendtochildselection (msg) = Selectedchild! Msg
Case Sendtoinnerchild (msg) = Innerchild foreach (Child = child! msg)
}
}
Object Backoffsupervisordemo extends App {
Import Parentalactor._
Val Testsystem = Actorsystem ("Testsystem")
Val parent = testsystem.actorof (parentalactor.props, "parent")
Thread.Sleep (//wait) for Backoffsupervisor.currentchild (ref) received
Parent! Sendtosupervisor (TestMessage ("Hello message 1 to supervisor"))
Parent! Sendtoinnerchild (TestMessage ("Hello message 2 to Innerchild"))
Parent! Sendtochildselection (TestMessage ("Hello message 3 to Selectedchild"))
Scala.io.StdIn.readLine ()
Testsystem.terminate ()
}
The results of the operation are as follows:
[INFO] [10/13/2018 16:11:48.167] [Testsystem-akka.actor.default-dispatcher-2] [Akka://testsystem/user/parent/supervisor/innerchild] Child received Message:hello message 1 to Supervisor[info] [10/13/2018 16:11:48.177] [testsystem-akka.actor.default-dispatcher-2] [ Akka://testsystem/user/parent/supervisor/innerchild] Child received Message:hello message 2 to Innerchild[info] [10/13 /2018 16:11:48.179] [testsystem-akka.actor.default-dispatcher-3] [akka://testsystem/user/parent/supervisor/ Innerchild] Child received Message:hello message 3 to Selectedchild
As can be seen from the results, although in the above example we send a message to supervisor,innerchild,selectedchild separately. But eventually all of the messages were responded by Innerchild.
The Backoffsupervisor of Akka