Est-ce que «tout en étant vrai» pour garder un script en vie est une bonne idée?

19

Je saute juste sous Unix dans un monde différent et je voulais savoir si

while true
do
  /someperlscript.pl
done

Le script perl lui-même possède en interne un observateur de dossier / fichier qui s'exécute lorsque les fichiers sont modifiés à l'emplacement cible.

Est-ce ( while true) une bonne idée? Sinon, quelle est la meilleure approche privilégiée?

TIA

EDIT: Étant donné que cela semble avoir généré un peu d'intérêt, voici le scénario complet. Le script perl lui-même surveille un répertoire à l'aide d'un observateur de fichiers. À la réception de nouveaux fichiers (ils arrivent via rsync), il récupère le nouveau et le traite. Maintenant, les fichiers entrants peuvent être corrompus (ne demandez pas .. provenant d'un Raspberry Pi), et parfois le processus peut ne pas être en mesure de le gérer. Je ne sais pas exactement pourquoi, car nous ne connaissons pas encore tous les scénarios.

MAIS - si le processus échoue pour une raison quelconque, nous voulons qu'il soit opérationnel et traite le fichier suivant, car le fichier suivant est complètement indépendant du précédent qui pourrait avoir causé l'erreur.

Habituellement, j'aurais utilisé une sorte de fourre-tout et enveloppé tout le code autour de lui pour qu'il ne se bloque JAMAIS. Mais n'était pas sûr pour Perl.

D'après ce que j'ai compris, utiliser quelque chose comme supervord est une bonne approche pour cela.

Abhinav Gujjar
la source
9
Est-ce Linux ou UNIX? Si c'est Linux, vous voudrez peut-être vérifier l' inotifyAPI afin d'éviter une boucle occupée attendant que les fichiers changent dans votre répertoire cible.
roaima
Its linux, ubuntu
Abhinav Gujjar
Si vous savez que le fichier est bon avant de quitter le pi, vous pouvez simplement exécuter une somme de contrôle comme md5 ou sha1 et l'envoyer avec votre fichier. Le destinataire saura alors s'il a un mauvais fichier avant d'essayer de le traiter. Si vous ne le savez pas, vous pouvez toujours créer une sorte de somme de contrôle partielle ou de bloc ou quelque chose de similaire dans votre fichier de données qui assure l'intégrité des données, afin que vous puissiez vérifier les choses au fur et à mesure avant de vous engager dans un processus qui peut échouer. Une once de prévention ...
Joe

Réponses:

21

Cela dépend de la vitesse à laquelle le script perl revient. S'il revient rapidement, vous voudrez peut-être insérer une petite pause entre les exécutions pour éviter la charge du processeur, par exemple:

while true
do
  /someperlscript.pl
  sleep 1
done

Cela empêchera également un porc CPU si le script n'est pas trouvé ou se bloque immédiatement.

La boucle pourrait également être mieux implémentée dans le script perl lui-même pour éviter ces problèmes.

Éditer:

Comme vous avez écrit la boucle, le seul but est de redémarrer le script perl en cas de panne, une meilleure approche serait de l'implémenter en tant que service surveillé, mais la façon précise de le faire dépend du système d'exploitation. Par exemple: Solaris smf, Linux systemd ou un redémarrage basé sur cron.

jlliagre
la source
10
Vous pourriez faire while sleep 1; do ...pour enregistrer le véritable appel.
Raphael Ahrens
4
@RaphaelAhrens En effet, cela changerait légèrement le comportement initial. L'alternative until ! sleep 1; do ...; doneenregistrera toujours cet appel intégré tout en démarrant le script immédiatement.
jlliagre
4
Totalement, je suis totalement d'accord pour dire qu'une boucle chaude doit être évitée avec un appel de sommeil à l'intérieur de la boucle si vous allez faire cela. Conviennent également qu'un script basé sur les événements (inotify, et al) serait une meilleure solution. Mais utiliser une boucle while n'est pas intrinsèquement mauvais, à moins qu'il ne soit infini et chaud. Je pense que le problème le plus important est probablement de savoir pourquoi le script perl échoue et doit être redémarré.
Craig
2
Mieux: sleep 1& /someperlscript.pl; wait.
user23013
2
@EliahKagan Bien repéré. TBH, je n'utilise jamais l' untilinstruction, je l'ai confondue avec une do/untilboucle hypothétique qui n'existe pas dans le shell.
jlliagre
13

Les autres réponses, concernant l'utilisation inotify, sont correctes, mais ne répondent pas à cette question.

Un superviseur de processus, tel que supervisord, upstartou runit, est conçu pour exactement le problème de surveillance et de redémarrage d'un service en cas de panne.

Votre distribution est probablement livrée avec un superviseur de processus intégré.

Jacob Krall
la source
11

while trueest bien comme une construction polyvalente "boucle pour toujours". Comme le disent d'autres réponses, le corps de la boucle ne doit pas être vide, ou devenir vide en raison de la commande à l'intérieur de la boucle qui ne fonctionne pas.

Si vous utilisez Linux, vous voudrez peut-être utiliser une commande comme inotifywait, ce qui rend la whileboucle beaucoup plus simple:

while inotifywait -qqe modify "$DIRECTORY"
do
    process_the_directory "$DIRECTORY"
done

Ici, la inotifywaitcommande va s'asseoir et attendre qu'un événement de système de fichiers se produise (dans cet exemple, lorsque les fichiers du répertoire sont écrits). À ce stade, il se termine avec succès et le corps de la boucle s'exécute. Il recommence ensuite à attendre. Parce que la inotifywaitcommande attend que quelque chose se produise dans le répertoire, elle est beaucoup plus efficace que d'interroger continuellement le répertoire.

Stuart Caie
la source
`(apt-get ou yum) installe inotify-tools` +1 cool!
JJoao
4

Déplacez le while 1 dans le script perl (suite à la suggestion de @roaima)

#!/usr/bin/perl

 use Linux::Inotify2;

 my $inotify = new Linux::Inotify2 or die "unable to inotify: $!";

 $inotify->watch ("Dir", IN_MODIFY, ## or in_{acess,create,open, etc...}
   sub { my $e = shift;
     my $name = $e->fullname;
     ## whatever 
     print "$name was modified\n" if $e->IN_MODIFY;
  });

 1 while $inotify->poll;
JJoao
la source
1
Cela ne répond pas à l'exigence de redémarrage si le script se bloque ou est tué pour une raison quelconque.
jlliagre
@jlliagre, merci pour le commentaire. Dans la réponse, je ne vois pas de spécification claire pour le comportement prévu après un crash ou un kill. Il s'agit clairement d'une question intéressante et pertinente. Pour l'instant, je vais rester simple (si le script est mort ou a été tué, restez mort :)
JJoao
@jlliagre C'est à cela que servent les exceptions. Mais Perl n'est peut-être pas le choix le plus approprié dans un tel cas car il ne prend pas en charge le mécanisme d'exception. Juste une supposition. Il serait préférable de porter le script dans un langage qui prend en charge les exceptions. Tout dépend de la taille du travail de portage, bien sûr.
@Nasha, En Perl, avec des gestionnaires de signaux, des variables d'erreur, Tyr :: Tiny, etc., nous pouvons le faire! ... mais - Je déteste la gestion des exceptions et la récupération des erreurs: elles ne sont jamais complètes ...
JJoao
1
@JJoao Je suis d'accord avec vous sur le fait que la récupération d'erreur est rarement complète. Il appartient bien sûr au développeur de couvrir tous les cas possibles. Il en va de même avec les automates dans le monde industriel, donc cela doit être tout à fait possible après tout ;-).
3

Lorsque votre script perl est destiné à continuer à fonctionner tout le temps, pourquoi utiliser la construction while? Lorsque Perl échoue en raison d'un problème grave, le nouveau script Perl démarré par while peut se bloquer tout aussi durement. Encore et encore et ainsi de suite.
si vous voulez vraiment recommencer votre perl, pensez à crontab et à un script qui vérifie d'abord les instances en cours d'exécution. De cette façon, votre script sera même lancé après un redémarrage.

Walter A
la source
2

En général, il n'y a aucun problème d'utilisation while truecar il s'agit d'un petit test qui n'est exécuté qu'après la fin du script perl. Gardez à l'esprit qu'en fonction de la variante Linux / Unix que vous utilisez, le script peut se terminer à la déconnexion. Dans ce cas, envisagez d'utiliser la boucle dans un script et appelez-la avec nohupet mettez-la en arrière-plan, c'est-à-direnohup myscript &

Si le script perl se termine trop souvent et provoque une charge CPU, cette charge est imputable au script perl et non au while true.

Voir aussi man nohuppour plus de détails.

Lambert
la source
Le seul problème est le potentiel d'une boucle chaude qui augmente l'utilisation du processeur. Je suis donc totalement d'accord pour mettre un appel de sommeil dans la boucle pour l'apprivoiser.
Craig
2

Si vous souhaitez gérer un processus, vous pouvez envisager un gestionnaire de processus pour le faire.

Avec de nombreux systèmes récents, vous pouvez utiliser systemd pour surveiller vos processus (ce qui est l'un des avantages de systemd par rapport aux scripts init classiques). Si votre distribution de choix n'utilise pas systemd, vous pouvez utiliser daemontools ou monit .

Andrew Aylett
la source
monest une alternative simple, si l'énormité accablante de monit et systemd vous dérange autant qu'ils me font.
Anko
2

La whileboucle n'est vraiment pas une bonne idée. Il n'y a pas d'échappatoire là-bas - cela fonctionne pour toujours - statiquement . Un certain nombre de choses pourraient changer dans l'environnement et cela ne sera pas affecté - et cela pourrait être mauvais.

Par exemple, si l'exécutable du shell responsable de cette whileboucle est mis à niveau, le noyau ne pourra pas libérer l'espace disque pour l'ancienne version tant que ce script ne se fermera pas car il doit maintenir le descripteur tant qu'il s'exécute. Et cela est vrai pour tous les fichiers que le shell peut avoir ouverts pour une raison quelconque pour exécuter la boucle - ils seront tous maintenus ouverts par cette whileboucle aussi longtemps qu'elle s'exécutera - pour toujours.

Et si, Dieu nous en préserve, il y a une fuite de mémoire n'importe où dans le shell exécutant cette boucle - même le plus petit - elle continuera simplement à fuir - statiquement . Il sera construit sans contrôle et le seul recours est de le tuer de force, puis de le recommencer juste pour faire la même chose plus tard.

Ce n'est vraiment pas la façon dont vous devez configurer un processus en arrière-plan - du moins, pas à mon avis. Au lieu de cela, comme je pense, il devrait y avoir un point de réinitialisation - une actualisation du script et de son état. La façon la plus simple de le faire est d'utiliser exec. Vous pouvez remplacer le processus actuel par un nouveau - tout en conservant le même PID - tout en exécutant un nouveau processus.

Par exemple, si votre perlscript doit renvoyer true après avoir correctement géré une modification de fichier dans un répertoire surveillé:

#!/bin/sh
trap 'rm -rf -- "${ldir%%*.}"' 0 INT
_exec() case    $#      in
        (0)     exec env -  "PID=$$" "ldir=${TMPDIR:-/tmp}/." \
                            "$0" "$@";;
        (*)     export "$@" "boff=0" "lmt=30"
                exec        "$0" "$@";;
        esac
[ "$PID" = "$$" ] || _exec
[ -w "$ldir" ]  &&
case    $ldir   in
(*.)    until   mkdir -- "$ldir"
        do      :& ldir=$ldir$$$!
        done    2>/dev/null
;;
(*)     until   /someperlscript.pl ||
                [ "$((boff+=1))"  -ge "$lmt" ]
        do      [ -d "$ldir" ]     &&
                sleep "$boff"      || ! break
        done
;;esac  &&      _exec ldir PID

...ou quelque chose comme ça. Quelque chose qui permet aux machines de base derrière la boucle de se rafraîchir de temps en temps.

mikeserv
la source
1

J'ai voté pour certaines de ces réponses, mais j'ai instinctivement les sentiments les plus chaleureux pour la réponse de @ WalterA. Eh bien, je l'ai fait jusqu'à ce que je crée ma propre réponse ...

Personnellement, j'aurais tendance à mettre à jour le script Perl pour qu'il écrive une entrée de journal descriptive sur l'échec et envoie une alerte à un administrateur.

Si le script Perl doit continuer à s'exécuter, en attendant les événements de modification du système de fichiers, pourquoi échoue-t-il?

Si ce n'est pas le cas, pourquoi vous souciez-vous de l'envelopper dans un script pour le redémarrer à l'infini?

S'il y a un problème de configuration ou une dépendance cassée qui provoque l'abandon du script Perl, il est peu probable que le redémarrer à plusieurs reprises le fasse soudainement fonctionner.

Vous connaissez la définition de la folie, non? (faire la même chose encore et encore, en attendant un résultat différent). Je dis juste. ;-)

Craig
la source
haha- je sais, je sais. mais vous savez - le monde réel est nul. Quoi qu'il en soit - la raison en est qu'il traite les fichiers, et certains fichiers peuvent même ne pas être transmis correctement. Nous ne connaissons pas encore la variété des raisons pour lesquelles cela peut échouer. Je vois votre point sur l'enregistrement de l'erreur, mais j'en
aurai
Si vous pouvez intercepter les exceptions, vous pouvez les enregistrer, puis continuer le traitement, et même revenir en arrière et réessayer de temps en temps les fichiers ayant échoué. Peut-être que le fichier en échec a été verrouillé par un autre utilisateur ou processus lorsque vous avez essayé de le traiter, etc.
Craig