Comment configurer un démon avec python-démon?

je suis nouveau dans daemons donc des excuses si c'est une question de débutant.

dans plusieurs autres réponses (par exemple, cette question ), les gens ont suggéré que le paquet python-daemon était la voie à suivre parce qu'il implémente entièrement la norme PEP 3143 .

malheureusement, python-daemon est un peu léger sur la documentation (ou plus probablement je suis un peu léger sur la connaissance / expérience... ;) ), et je pense que je suis probablement quelque chose lui manque vraiment de base. Voilà ce que je fais:

j'ai le suivant:

import daemon

logfile = open('daemon.log', 'w')

context = daemon.DaemonContext(stdout = logfile, stderr = logfile)

context.open()

with context:
    do_something_1()
    do_something_2()

Question: Comment configurer un démon avec python-démon, Comment puis-je le démarrer et l'arrêter?


notes:

je suis fondamentalement en train de deviner comment / si la méthode .open() devrait être utilisé ici -- les documents n'étaient pas très clairs sur ce point. La même chose semble se produire, que je l'inclue ou non.

donc, maintenant que dois-je faire? Lorsque j'essaie d'exécuter ce fichier, par exemple:

python startConsumerDaemons.py

il semble s'exécuter do_something_1() , mais pas le second. Et, il semble laisser le programme attaché à la fenêtre du terminal. IE, stdout n'est pas redirigé, et quand je ferme la fenêtre du terminal le processus est tué. Donc, je suis assez sûr que je suis en train de faire quelque chose de mal ici... que dois-je faire différemment?

et, enfin, une fois que j'ai activé le démon, comment l'arrêter / le redémarrer (par exemple si je modifie le code sous-jacent)?

28
demandé sur Community 2012-10-28 07:49:38

6 réponses

Voici ce que j'ai, qui fonctionne pour moi. Il a aussi un script sysvinit. Repo est à GitHub , et j'ai aussi un bref billet de blog avec des liens vers d'autres solutions possibles que j'ai trouvé.

il ne peut y avoir qu'un seul processus de démon en cours d'exécution: il est géré par le fichier PID lock, comme la plupart des autres démons Linux. Pour l'arrêter, faites

kill `cat /var/run/eg_daemon.pid`

pour voir si elle tourne:

ps -elf | grep `cat /var/run/eg_daemon.pid`

à l'aide du sous-module pidfile, le fichier PID est géré automatiquement. Lorsque le démon est arrêté, le fichier pidfile est effacé. S'il vous plaît voir le lien GitHub repo pour le script d'initialisation.

voici le code de démon Python:

#!/usr/bin/env python3.5
import sys
import os
import time
import argparse
import logging
import daemon
from daemon import pidfile

debug_p = False

def do_something(logf):
    ### This does the "work" of the daemon

    logger = logging.getLogger('eg_daemon')
    logger.setLevel(logging.INFO)

    fh = logging.FileHandler(logf)
    fh.setLevel(logging.INFO)

    formatstr = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    formatter = logging.Formatter(formatstr)

    fh.setFormatter(formatter)

    logger.addHandler(fh)

    while True:
        logger.debug("this is a DEBUG message")
        logger.info("this is an INFO message")
        logger.error("this is an ERROR message")
        time.sleep(5)


def start_daemon(pidf, logf):
    ### This launches the daemon in its context

    ### XXX pidfile is a context
    with daemon.DaemonContext(
        working_directory='/var/lib/eg_daemon',
        umask=0o002,
        pidfile=pidfile.TimeoutPIDLockFile(pidf),
        ) as context:
        do_something(logf)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Example daemon in Python")
    parser.add_argument('-p', '--pid-file', default='/var/run/eg_daemon.pid')
    parser.add_argument('-l', '--log-file', default='/var/log/eg_daemon.log')

    args = parser.parse_args()

    start_daemon(pidf=args.pid_file, logf=args.log_file)

pour être complet, voici le script d'entrée. Notez que "kill" est vraiment juste une méthode pour envoyer un signal POSIX -- voir la page de manuel pour le signal(7) pour un aperçu. Le contexte python-daemon attrapera le signal, terminer le processus en fermant proprement les descripteurs de fichier, et supprimer le fichier PID automatiquement. C'est donc vraiment un arrêt propre.

vous pouvez écrire votre code pour attraper SIGUSR1 ou quelque chose de similaire, afin de faire un rechargement de la configuration du démon. Il n'y a aucun avantage à écrire Python arrêter le démon.

#!/bin/bash
#
# eg_daemon      Startup script for eg_daemon
#
# chkconfig: - 87 12
# description: eg_daemon is a dummy Python-based daemon
# config: /etc/eg_daemon/eg_daemon.conf
# config: /etc/sysconfig/eg_daemon
# pidfile: /var/run/eg_daemon.pid
#
### BEGIN INIT INFO
# Provides: eg_daemon
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Short-Description: start and stop eg_daemon server
# Description: eg_daemon is a dummy Python-based daemon
### END INIT INFO

# Source function library.
. /etc/rc.d/init.d/functions

if [ -f /etc/sysconfig/eg_daemon ]; then
        . /etc/sysconfig/eg_daemon
fi

eg_daemon=/var/lib/eg_daemon/eg_daemon.py
prog=eg_daemon
pidfile=${PIDFILE-/var/run/eg_daemon.pid}
logfile=${LOGFILE-/var/log/eg_daemon.log}
RETVAL=0

OPTIONS=""

start() {
        echo -n $"Starting $prog: "

        if [[ -f ${pidfile} ]] ; then
            pid=$( cat $pidfile  )
            isrunning=$( ps -elf | grep  $pid | grep $prog | grep -v grep )

            if [[ -n ${isrunning} ]] ; then
                echo $"$prog already running"
                return 0
            fi
        fi
        $eg_daemon -p $pidfile -l $logfile $OPTIONS
        RETVAL=$?
        [ $RETVAL = 0 ] && success || failure
        echo
        return $RETVAL
}

stop() {
    if [[ -f ${pidfile} ]] ; then
        pid=$( cat $pidfile )
        isrunning=$( ps -elf | grep $pid | grep $prog | grep -v grep | awk '{print }' )

        if [[ ${isrunning} -eq ${pid} ]] ; then
            echo -n $"Stopping $prog: "
            kill $pid
        else
            echo -n $"Stopping $prog: "
            success
        fi
        RETVAL=$?
    fi
    echo
    return $RETVAL
}

reload() {
    echo -n $"Reloading $prog: "
    echo
}

# See how we were called.
case "" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  status)
    status -p $pidfile $eg_daemon
    RETVAL=$?
    ;;
  restart)
    stop
    start
    ;;
  force-reload|reload)
    reload
    ;;
  *)
    echo $"Usage: $prog {start|stop|restart|force-reload|reload|status}"
    RETVAL=2
esac

exit $RETVAL
9
répondu phzx_munki 2016-11-13 16:51:16

Un exemple complet est disponible ici .

vous devriez être capable de mieux comprendre le fonctionnement interne de python-daemon.

de plus le code fourni donne aussi un exemple de script init pour simplement démarrer/arrêter le démon. Cependant, vous pouvez le démarrer / arrêter simplement en appelant à nouveau la fonction d'origine avec l'argument stop:

python original_func.py stop
5
répondu gromain 2013-02-25 18:21:18

comme vous pouvez le voir dans le " avec "la documentation de la déclaration , il Déclaration effectue une certaine "magie", qui est liée à notre but. Plus précisément:

L'exécution de l'instruction avec un "élément" produit

  1. l'expression de contexte (l'expression donnée dans le with_item) est évaluée pour obtenir un gestionnaire de contexte.

  2. le gestionnaire de contexte __exit__() est chargé pour une utilisation ultérieure.

  3. la méthode __enter__() du gestionnaire de contexte est invoquée.

  4. si une cible a été incluse dans la déclaration with, la valeur de retour de __enter__() lui est attribuée.

  5. la suite est exécutée.

  6. la méthode __exit__() du gestionnaire de contexte est invoquée. Si une exception a causé la sortie de la suite, son type, sa valeur, et traceback sont passés comme arguments à __exit__() . Sinon, Aucun des trois les arguments sont fournis.

Qu'est-ce que cela signifie? Si vous regardez attentivement le PEP en question , qui sert aussi de documentation python-daemon (et qui pourrait en effet être amélioré), vous verrez qu'il met en œuvre __enter__() et __exit__() :

la classe implémente également le protocole de gestionnaire de contexte via __enter__ et __exit__ méthodes.

__enter__()

appelle la méthode open() de l'instance, puis renvoie l'instance.

__exit__(exc_type, exc_value, exc_traceback)

appelle la méthode close() de l'instance, puis renvoie True si l'exception a été traitée ou False si elle ne l'a pas été.

en d'autres termes, open() n'est pas nécessaire, l'exemple donné dans la PEP (bien qu'il ne soit pas expliqué correctement) fonctionne tel quel. Alors que l'instruction with signifie quelque chose, elle ne garde aucune boucle, une fois la fin de sa portée atteinte, elle appelle exit (), ce qui en python-daemon signifie close(). Par conséquent, vous avez besoin de mettre Il ya un moment vrai ou qui jamais boucle infinie que vous considérez.

sur behave of your second script ne fonctionne pas, Je ne peux pas vraiment vous dire, je suis surpris que le premier fonctionne déjà. Si votre démon s'arrête, il y a un problème avec vos scripts pour sûr, vous pouvez vérifier votre consumerDaemonLogFile. (comme une note latérale, vous avez une typo 'sderr' --> 'stderr')

aussi, vous pouvez voir dans le PEP que si non spécifié, la propriété de répertoire de travail par défaut à "/ " . ce pourrait être la source de votre problème si vous utilisez des chemins relatifs dans vos scripts.

enfin, à propos de la dernière question, vous pouvez facilement tuer vous êtes démon trouver son PID:

ps ax | grep startConsumerDaemons.py

et en lui envoyant un SIGTERM:

kill <pid>

la réponse fournie par gromain ne fournit un moyen plus pratique pour commencer et arrêter, avec 'daemon.runner ()', mais c'est beaucoup plus compliqué à configurer.

2
répondu ArnauOrriols 2013-10-03 02:42:45

sous linux, vous pouvez arrêter le démon en exécutant:

$ ps -x

et trouver le PID qui correspond à votre démon et ensuite juste tuer le processus.

0
répondu Joseph Feeney 2012-12-19 00:26:11

il manque encore une documentation utile pour le module"python-daemon". J'ai personnellement renoncé à l'utiliser, et maintenant j'utilise avec succès le code de démon de Sander Marechal référencé dans cette réponse .

Je l'ai légèrement modifié pour pouvoir faire des choses quand vous appelez python testdaemon.py stop .

voici le code .


Exemple d'utilisation:

import sys, daemon, time

class testdaemon(daemon.Daemon):
    def run(self):
        self.i = 0
        with open('test1.txt', 'w') as f:
            f.write(str(self.i))
        while True:
            self.i += 1
            time.sleep(1)

    def quit(self):
        with open('test2.txt', 'w') as f:
            f.write(str(self.i))

daemon = testdaemon()

if 'start' == sys.argv[1]: 
    daemon.start()
elif 'stop' == sys.argv[1]: 
    daemon.stop()
elif 'restart' == sys.argv[1]: 
    daemon.restart()
0
répondu Basj 2017-05-23 12:10:35

le constructeur daemon.DaemonContext accepte un paramètre lockfile . Utilisez une bibliothèque lockfile qui enregistrera le PID du processus, comme lockfile.PIDLockFile .

ensuite, le PID du processus est trouvé simplement en lisant le contenu du fichier PID nommé. Utilisez ce PID pour envoyer des signaux à votre démon courant.

-1
répondu bignose 2016-02-01 23:51:30