2013-07-22 1 views
8

Comment gérer une exception lancée par le DbActor ici? Je ne suis pas sûr de savoir comment le gérer, devrait conduire le cas d'échec?Comment gérer l'exception avec le modèle de demande et la supervision

class RestActor extends Actor with ActorLogging { 
    import context.dispatcher 

    val dbActor = context.actorOf(Props[DbActor]) 
    implicit val timeout = Timeout(10 seconds) 


    override val supervisorStrategy: SupervisorStrategy = { 
    OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) { 
     case x: Exception => ??? 
    } 
    } 

    def receive = { 
    case GetRequest(reqCtx, id) => { 

     // perform db ask 
     ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete { 
     case Success(obj) => { // some stuff } 
     case Failure(err) => err match { 
      case x: Exception => ??? 
     } 
     } 
    } 
    } 
} 

Nous serions heureux d'avoir votre avis, merci d'avance!

Répondre

6

Il y a quelques questions que je peux voir ici sur la base des questions dans votre exemple de code:

  1. 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?

  2. 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:

  1. Stop - arrêter complètement l'acteur de l'enfant et de ne pas envoyer d'autres messages à ce

  2. Reprise - Reprendre l'enfant a échoué, pas redémarrer gardant ainsi son état interne actuel

  3. 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)

  4. 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 
+0

Je pense que je comprends, la vraie clé ici est de comprendre ce qui n'est pas manipulé – graph1ZzLle

3

Il existe un modèle Activator (http://www.typesafe.com/activator) qui illustre un bon exemple de lien entre demande et supervision: http://www.typesafe.com/activator/template/akka-supervision.

+0

merci, je vais vérifier cela – graph1ZzLle

+0

btw: l'essentiel de cet exemple est que l'acteur parent conserve une carte des expéditeurs et des acteurs travailleurs ('Map [ActorRef, ActorRef]') afin qu'il sache qui informer dans le stratégie de supervision lorsqu'une exception survient chez l'enfant. – rethab

0

Pour les systèmes plus simples, on peut vouloir attraper et transmettre toutes les erreurs. Pour que je fait cette petite fonction pour envelopper la méthode receive, sans prendre la peine de supervision:

import akka.actor.Actor.Receive 
    import akka.actor.ActorContext 
    /** 
    * Meant for wrapping the receive method with try/catch. 
    * A failed try will result in a reply to sender with the exception. 
    * @example 
    *   def receive:Receive = honestly { 
    *   case msg => sender ! riskyCalculation(msg) 
    *   } 
    *   ... 
    *   (honestActor ? "some message") onComplete { 
    *   case e:Throwable => ...process error 
    *   case r:_ => ...process result 
    *   } 
    * @param receive 
    * @return Actor.Receive 
    * 
    * @author Bijou Trouvaille 
    */ 
    def honestly(receive: =>Receive)(implicit context: ActorContext):Receive = { case msg => 
     try receive(msg) catch { case error:Throwable => context.sender ! error } 
    } 

alors vous pouvez le placer dans un fichier de package et importer la akka.pattern.pipe et tel. Évidemment, cela ne traitera pas des exceptions lancées par du code asynchrone.

Questions connexes