Gestion des exceptions dans un programme qui doit s'exécuter 24/7

14

J'ai lu que nous ne devrions attraper que les exceptions qui peuvent être gérées, ce qui rend la capture de la classe d'exception de base (C # dans ce cas) une mauvaise idée (en plus d'autres raisons). Je fais actuellement partie d'un projet dans lequel je n'ai jusqu'à présent rien vu d'autre que l'exception de base en cours de capture. J'ai mentionné que cela est considéré comme une mauvaise pratique de le faire, mais la réponse a été: "Ce service doit fonctionner 24 heures sur 24, 7 jours sur 7, c'est ainsi."

Comme je n'ai pas eu de bonne réponse sur la façon de gérer correctement les exceptions dans un programme qui doit s'exécuter 24h / 24 et 7j / 7, je suis maintenant ici. Je n'ai pas réussi à trouver d'informations / suggestions sur la façon de gérer la gestion des exceptions dans les programmes / services "critiques" qui doivent fonctionner 24h / 24 (et dans ce cas, je pense que cela peut être correct si le service est arrêté pendant une minute) ou deux, donc même pas critique). Je comprends que cela dépend de la nature exacte du programme. Les exigences pour un programme qui peut causer des problèmes mortels sont très différentes par rapport à un scanner de journaux pour un jeu en ligne.

Deux exemples:

1: Un service de saisie anticipée pour les clients des chemins de fer britanniques, utilisé lorsqu'ils recherchent en ligne des gares.

2: Un programme qui contrôle automatiquement les commutateurs ferroviaires pour les chemins de fer ci-dessus en fonction des informations en temps réel fournies par divers capteurs dans les voies, les trains, etc.

Le premier programme ne poserait probablement pas de problème majeur s'il était interrompu pendant une minute ou deux, alors que ce dernier pourrait provoquer des pertes humaines. Des suggestions sur la façon de traiter chacun d'eux? Pointeur vers où trouver plus d'informations et de réflexions sur ce problème?

user1323245
la source
2
Le déroulement de la pile pendant la gestion des exceptions dans une application en temps réel (sic!) Peut détruire un train.
Deer Hunter
4
@DeerHunter Un mauvais codage sans exception peut avoir le même résultat.
BЈовић
9
D'accord, donc vous catch Exception. Cela ne signifie pas que votre programme fonctionne , cela signifie que les échecs permettent à l'état de l'application d'être corrompu pendant qu'il continue à s'exécuter, un endroit beaucoup plus dangereux. Un programme en panne peut être désastreux, mais un programme qui n'est pas valide mais qui exécute toujours des actions peut être activement désastreux.
Phoshi
1
Si l'application doit fonctionner 24h / 24 et 7j / 7, il y a une boucle infinie quelque part et cette boucle infinie ferait mieux d'être enroulée autour d'une construction qui capture toutes les exceptions non gérées. Si ce n'est pas le cas, une exception non gérée se répercutera sur le gestionnaire fourre-tout déjà existant qui est en dehors de main et kaboom! l'application 24/7 se termine.
David Hammen

Réponses:

7

Certaines fonctionnalités linguistiques comme

  • Collecte des ordures
  • Systèmes d'exception
  • Évaluation paresseuse

ne sont généralement pas utiles dans un système en temps réel. Il faut probablement choisir une langue sans ces fonctionnalités et essayer de prouver certaines propriétés comme l'utilisation maximale de la mémoire ou le temps de réponse maximum.


Lorsqu'un programme doit s'exécuter en continu, mais que des défaillances courtes et non globales sont acceptables, nous pourrions alors utiliser une stratégie de type Erlang. Erlang est un langage de programmation fonctionnel simultané. Habituellement, un programme écrit en Erlang se composera de plusieurs processus de travail qui peuvent communiquer entre eux (modèle d'acteur). Si un thread de travail rencontre une exception, il est redémarré. Bien que cela implique un court temps d'arrêt, les autres acteurs peuvent continuer comme d'habitude.

Pour résumer ceci: Dans un programme robuste, différentes parties sont isolées les unes des autres et peuvent être redémarrées ou mises à l'échelle indépendamment.

Donc, fondamentalement, nous avons besoin d'un morceau de code équivalent à ceci:

while (true) {
  try {
    DoWork();
  }
  catch (Exception e) {
    log(e);
  }
}

plus un moyen de terminer la boucle. Une telle boucle entraînerait alors chaque thread de travail.


Un problème avec l'ignorance des erreurs via un fourre-tout est que les invariants de votre programme peuvent avoir été violés par la cause de l'erreur et que les opérations suivantes peuvent être inutiles. Une bonne solution à cela est de ne partager aucune donnée entre les travailleurs indépendants. Le redémarrage d'un travailleur reconstruira tous les invariants nécessaires. Cela signifie qu'ils doivent communiquer différemment, par exemple par l'envoi de messages. L'état d'un acteur peut ne pas faire partie des invariants des autres acteurs.

Un autre problème lié à la capture d'un trop grand nombre d'exceptions est que toutes les exceptions ne peuvent pas être corrigées en redémarrant, même en prenant de telles précautions. Dans le cas contraire, des problèmes difficiles comme le manque de mémoire peuvent être traités en redémarrant. Mais un redémarrage ne vous aidera pas à retrouver la connectivité Internet lorsqu'un câble physique a été débranché.

amon
la source
1
Oui, mais la situation comme un "câble physique a été retiré" est exactement celle où vous voulez que le journal des exceptions se remplisse jusqu'à ce que quelqu'un remette le câble, puis les choses recommencent à fonctionner, sans redémarrage manuel supplémentaire de l'application.
Mark Hurd
2

Pour répondre à votre question, il faut comprendre quelles sont les exceptions et comment elles fonctionnent.

Des exceptions sont généralement levées lorsque de telles erreurs se produisent, lorsque l'assistance de l'utilisateur est requise. Dans de tels cas, peu importe le temps qu'il faut pour dérouler la pile et gérer l'exception.

Sans gestionnaires de captures, le programme arrête l'exécution. Selon votre configuration et vos exigences, cela peut être acceptable.

Dans vos cas spécifiques:

  1. si la requête ne peut pas être exécutée (par exemple, nom de ville incorrect), informez l'utilisateur de l'erreur et demandez à la corriger.
  2. si vous n'obtenez pas d'informations d'un capteur critique, il est inutile de continuer sans demander à l'opérateur de résoudre le problème.

Cela signifie que dans les deux cas, il peut être judicieux d'utiliser des exceptions, avec plus de soin dans un programme RT pour indiquer uniquement les problèmes graves où il n'est pas possible de poursuivre l'exécution.

BЈовић
la source
1

Jusqu'à présent, je n'ai encore rien vu, à l'exception de l'exception de base.

Il semble qu'il y ait un problème ici, dans la mesure où les exceptions ne sont pas traitées de manière appropriée. La capture d'exceptions au moment approprié et la prise de mesures appropriées (selon le type d'exception) permettront au service de fonctionner de manière beaucoup plus fiable.

Si le service doit continuer, il est probablement important qu'il fonctionne comme prévu. Dans votre exemple, si un programme qui contrôle les aiguillages ferroviaires lève une exception, cela peut indiquer qu'il y a un problème de communication avec les capteurs liés à la sécurité. Si vous interceptez l'exception de base et continuez, le service peut s'exécuter, mais peut ne pas fonctionner comme prévu, entraînant un sinistre.

Alternativement, si vous interceptez l'exception levée en cas d'échec de communication avec le capteur et que vous le gérez correctement (par exemple, arrêtez les trains dans la zone affectée), votre service fonctionne et vous n'avez tué personne.

Donc, si je comprends bien la question, je suggère que dans un premier temps, vous feriez mieux de chercher à ajouter une gestion des exceptions plus spécifique plutôt que de supprimer les gestionnaires de type exception de base.

Mat
la source
0

En ce qui concerne le point 2: n'utilisez pas C #. Ce n'est pas une langue en temps réel et vous fera vous blesser si vous essayez de l' utiliser en tant que tel.

Pour le point 1: vous pouvez suivre la voie erlang: laissez-la planter, puis redémarrez

miniBill
la source
Mon utilisation et mon expertise en C # ne concernent pas le point 2 (changement de piste en temps réel). Je suis curieux de savoir pourquoi C # est si inadapté à une telle tâche?
Michael O'Neill
1
Surtout: le garbage collector rend le comportement du programme, en ce qui concerne le temps, imprévisible. De plus, le temps d'exécution est trop complexe, et dans ces contextes, vous avez besoin de choses simples, elles sont plus prévisibles
miniBill
0

Declaimer: ce ne sont que des pensées, je n'ai pas l'expérience.

Je suppose qu'un programme satisfaisant aux exigences du deuxième exemple devrait être extrêmement modulaire . Par conséquent, les modules pourront être redémarrés, sans déstabiliser le système.

Par exemple, un objet, à défaut d'une affirmation pour l'état interne, devrait pouvoir être détruit et recréé, notifiant dans le processus tous ses consommateurs et fournisseurs. Plus concrètement, si le programme contrôle les commutateurs du chemin de fer et échoue à une assertion dans la boucle de décision, il peut toujours exécuter un module d'urgence, qui arrête tous les trains impliqués et attend que le module de décision principal se réinitialise.

Plus réaliste, on introduirait une redondance - duplication du matériel et des logiciels. Une instance est câblée au système contrôlé et l'autre est libre. Si une erreur est détectée, les systèmes sont commutés.

Un exemple est deux processus sur la même machine, qui se surveillent mutuellement et si l'un est tué, l'autre le réapparaît et dissocie son PID parent de lui-même.

Vorac
la source