Il y a quelques questions que je peux voir ici sur la base des questions dans votre exemple de code:
Quels types de choses que je peux faire quand je remplacer le comportement du superviseur par défaut dans la définition de comment gérer les exceptions?
Lorsque vous utilisez ask
, quels types de choses que je peux faire quand je reçois un résultat Failure
sur le Future
que je suis en attente sur?
Commençons par la première question d'abord (généralement une bonne idée). Lorsque vous remplacez la stratégie de superviseur par défaut, vous pouvez modifier la manière dont certains types d'exceptions non gérées dans l'acteur enfant sont traitées en ce qui concerne ce qu'il faut faire avec cet acteur enfant ayant échoué. Le mot clé dans cette phrase précédente est unhandled
. Pour les acteurs qui effectuent une requête/réponse, vous pouvez réellement gérer (attraper) des exceptions spécifiques et renvoyer certains types de réponses à la place (ou échouer le futur en amont, plus sur cela plus tard) plutôt que de les laisser non traitées. Lorsqu'une exception non gérée se produit, vous perdez la possibilité de répondre à l'expéditeur avec une description du problème et l'expéditeur obtiendra probablement un TimeoutException
à la place car son Future
ne sera jamais terminé. Une fois que vous avez compris ce que vous gérez explicitement, vous pouvez considérer toutes les autres exceptions lors de la définition de votre stratégie de supervision personnalisée. A l'intérieur de ce bloc ici:
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
case x: Exception => ???
}
Vous obtenez une chance de mapper un type d'exception à une défaillance Directive
, qui définit comment sera géré l'échec du point de vue de la surveillance.Les options sont:
Stop - arrêter complètement l'acteur de l'enfant et de ne pas envoyer d'autres messages à ce
Reprise - Reprendre l'enfant a échoué, pas redémarrer gardant ainsi son état interne actuel
Restart - Comme pour reprendre, mais dans ce cas, l'ancienne instance est jeté et une nouvelle instance est construit et l'état interne est remis à zéro (prédémarrage)
Escalate - Escaladez up la chaîne au parent du superviseur
Disons que, étant donné un SQLException
que vous vouliez reprendre et étant donné tous les autres que vous souhaitez redémarrer ensuite votre code ressemblerait à ceci:
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
case x: SQLException => Resume
case other => Restart
}
maintenant pour la deuxième question qui concerne ce qu'il faut faire lorsque le Future
lui-même renvoie une réponse Failure
. Dans ce cas, je suppose que cela dépend de ce qui était censé arriver à la suite de cette Future
. Si l'acteur reste lui-même était chargé de remplir la demande http (disons que httpCtx a une fonction complete(statusCode:Int, message:String)
dessus), alors vous pourriez faire quelque chose comme ceci:
ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
case Success(obj) => reqCtx.complete(200, "All good!")
case Failure(err:TimeoutException) => reqCtx.complete(500, "Request timed out")
case Failure(ex) => reqCtx.complete(500, ex.getMessage)
}
Maintenant, si un autre acteur était en amont responsable de l'achèvement du demande http et vous avez besoin pour répondre à cet acteur, vous pouvez faire quelque chose comme ceci:
val origin = sender
ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
case Success(obj) => origin ! someResponseObject
case Failure(ex) => origin ! Status.Failure(ex)
}
cette approche suppose que dans le bloc de succès que vous voulez d'abord masser l'objet de résultat avant de répondre. Si vous ne voulez pas faire et que vous voulez reporter le résultat de manipulation à l'expéditeur alors vous pouvez faire:
val origin = sender
val fut = ask(dbActor, ReadCommand(reqCtx, id))
fut pipeTo origin
Je pense que je comprends, la vraie clé ici est de comprendre ce qui n'est pas manipulé – graph1ZzLle