web-dev-qa-db-fra.com

Comment exécuter des processus à long terme (infinis) Python?

J'ai récemment commencé à expérimenter l'utilisation de Python pour le développement Web. Jusqu'à présent, j'ai réussi à utiliser Apache avec mod_wsgi et le cadre Web Django pour = Python 2.7. Cependant, j'ai rencontré des problèmes avec les processus en cours d'exécution, la mise à jour des informations, etc.).

J'ai écrit un script que j'appelle "daemonManager.py" qui peut démarrer et arrêter toutes ou des boucles de mise à jour individuelles python (Dois-je les appeler Daemons?). Il le fait en forçant, puis en chargeant le module pour les fonctions spécifiques qu'il doit exécuter et démarrer une boucle infinie. Il enregistre un fichier PID dans /var/run pour suivre le processus. Jusqu'ici tout va bien. Les problèmes que j'ai rencontrés sont:

  • De temps en temps, l'un des processus s'arrête. Je vérifie ps le matin et le processus vient de disparaître. Aucune erreur n'a été enregistrée (j'utilise le module logging), et je couvre toutes les exceptions auxquelles je peux penser et je les enregistre. De plus, je ne pense pas que ces processus de fermeture aient quelque chose à voir avec mon code, car tous mes processus exécutent un code complètement différent et se terminent à des intervalles assez similaires. Je peux me tromper, bien sûr. Est-il normal que les processus Python meurent juste après avoir fonctionné pendant des jours/semaines? Comment dois-je résoudre ce problème? Dois-je écrire un autre démon qui vérifie périodiquement si les autres démons sont toujours en cours d'exécution) "Et si ce démon s'arrête? Je ne sais pas comment gérer cela.".

  • Comment puis-je savoir par programme si un processus est toujours en cours d'exécution ou non? J'enregistre les fichiers PID dans /var/run et vérifier si le fichier PID est là pour déterminer si le processus est en cours d'exécution ou non. Mais si le processus meurt juste de causes inattendues, le fichier PID restera. Je dois donc supprimer ces fichiers à chaque fois qu'un processus plante (quelques fois par semaine), ce qui en quelque sorte va à l'encontre du but. Je suppose que je pourrais vérifier si un processus est en cours d'exécution au PID dans le fichier, mais que se passe-t-il si un autre processus a démarré et a reçu le PID du processus mort? Mon démon penserait que le processus fonctionne bien même s'il est mort depuis longtemps. Encore une fois, je ne sais pas comment faire face à cela.

Toute réponse utile sur la façon optimale d'exécuter des processus infinis Python, en espérant également faire la lumière sur les problèmes ci-dessus, j'accepterai


J'utilise Apache 2.2.14 sur une machine Ubuntu.
Ma Python est 2.7.2

31
Hubro

Je vais commencer en déclarant que c'est un moyen de gérer un processus de longue durée (LRP) - pas de facto par n'importe quel tronçon.

D'après mon expérience, le meilleur produit possible vient de la concentration sur le problème spécifique que vous rencontrez, tout en déléguant la technologie de support à d'autres bibliothèques. Dans ce cas, je fais référence à l'acte des processus d'arrière-plan (l'art de la double fourche), de la surveillance et de la redirection des journaux.

Ma solution préférée est http://supervisord.org/

En utilisant un système comme supervisord, vous écrivez essentiellement un script python classique qui exécute une tâche tout en étant coincé dans une boucle "infinie").

#!/usr/bin/python

import sys
import time

def main_loop():
    while 1:
        # do your stuff...
        time.sleep(0.1)

if __name__ == '__main__':
    try:
        main_loop()
    except KeyboardInterrupt:
        print >> sys.stderr, '\nExiting by user request.\n'
        sys.exit(0)

L'écriture de votre script de cette façon le rend simple et pratique à développer et à déboguer (vous pouvez facilement le démarrer/l'arrêter dans un terminal, en regardant la sortie du journal au fur et à mesure que les événements se déroulent). Quand vient le temps de se lancer dans la production, vous définissez simplement une configuration de superviseur qui appelle votre script (voici l'exemple complet pour définir un "programme", beaucoup dont facultatif: http://supervisord.org/configuration.html#program-x-section-example ).

Le superviseur a un tas d'options de configuration donc je ne les énumérerai pas, mais je dirai que cela résout spécifiquement les problèmes que vous décrivez:

  • Contexte/Démonisation
  • Suivi PID (peut être configuré pour redémarrer un processus s'il se termine de façon inattendue)
  • Connectez-vous normalement dans votre script (gestionnaire de flux si vous utilisez le module de journalisation plutôt que l'impression) mais laissez le superviseur rediriger vers un fichier pour vous.
26
Owen Nelson

Je suppose que vous utilisez Unix/Linux mais vous ne le dites pas vraiment. Je n'ai aucun conseil direct sur votre problème. Je ne m'attends donc pas à être la "bonne" réponse à cette question. Mais il y a quelque chose à explorer ici.

Tout d'abord, si vos démons se bloquent, vous devez corriger cela. Seuls les programmes contenant des bogues devraient planter. Vous devriez peut-être les lancer sous un débogueur et voir ce qui se passe quand ils plantent (si c'est possible). Avez-vous des traces de journalisation dans ces processus? Sinon, ajoutez-les. Cela pourrait aider à diagnostiquer votre crash.

Deuxièmement, vos démons fournissent-ils des services (ouverture de canaux et attente de demandes) ou effectuent-ils un nettoyage périodique? S'il s'agit de processus de nettoyage périodiques, vous devez utiliser cron pour les lancer périodiquement plutôt que de les exécuter dans une boucle infinie. Les processus Cron doivent être préférés aux processus démon. De même, s'il s'agit de services qui ouvrent des ports et des demandes de service, avez-vous envisagé de les faire fonctionner avec INETD? Encore une fois, un seul démon (inetd) devrait être préféré à un tas de processus démon.

Troisièmement, l'enregistrement d'un PID dans un fichier n'est pas très efficace, comme vous l'avez découvert. Peut-être qu'un IPC partagé, comme un sémaphore, fonctionnerait mieux. Je n'ai pas de détails ici cependant.

Quatrièmement, j'ai parfois besoin de choses à exécuter dans le contexte du site Web. J'utilise un processus cron qui appelle wget avec une URL de maintenance. Vous définissez un cookie spécial et incluez les informations de cookie dans la ligne de commande wget. Si le cookie spécial n'existe pas, retournez 403 plutôt que d'effectuer le processus de maintenance. L'autre avantage ici est la connexion à la base de données et les autres problèmes environnementaux évités, car le code qui dessert les pages Web normales sert le processus de maintenance.

J'espère que cela vous donne des idées. Je pense que si vous le pouvez, éviter les démons est le meilleur endroit pour commencer. Si vous pouvez exécuter votre python dans mod_wsgi, cela vous évite d'avoir à prendre en charge plusieurs "environnements". Le débogage d'un processus qui échoue après une exécution de plusieurs jours est tout simplement brutal.

2
jmucchiello

Vous devriez considérer les processus Python comme capables de s'exécuter "indéfiniment" en supposant que vous n'avez aucune fuite de mémoire dans votre programme, l'interpréteur Python, ou l'un des les Python bibliothèques/modules que vous utilisez. (Même face à des fuites de mémoire, vous pourrez peut-être exécuter indéfiniment si vous avez suffisamment d'espace de swap sur une machine 64 bits. Des décennies, sinon des siècles, cela devrait être faisable. J'ai eu Python survivent très bien pendant près de deux ans sur un matériel limité - avant que le matériel ne doive être déplacé.)

Assurer le redémarrage des programmes quand ils meurent était très simple lorsque les distributions Linux étaient utilisées style SysV init - vous venez d'ajouter une nouvelle ligne au /etc/inittab Et init(8) lancerait votre programme au démarrage et le réapparaîtrait s'il meurt. (Je ne connais aucun mécanisme pour répliquer cette fonctionnalité avec le nouveau remplacement upstartinit- que de nombreuses distributions utilisent de nos jours. Je ne dis pas que c'est impossible , Je ne sais pas comment faire.)

Mais même le mécanisme init(8) des années passées n'était pas aussi flexible que certains l'auraient souhaité. Le paquet daemontools de DJB est un exemple d'outils de contrôle et de surveillance de processus destinés à maintenir les démons en vie pour toujours. La suite Linux-HA fournit un autre outil similaire, bien qu'il puisse fournir trop de fonctionnalités "supplémentaires" pour être justifié pour cette tâche. monit est une autre option.

2
sarnold