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)?
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
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
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
l'expression de contexte (l'expression donnée dans le with_item) est évaluée pour obtenir un gestionnaire de contexte.
le gestionnaire de contexte
__exit__()
est chargé pour une utilisation ultérieure.la méthode
__enter__()
du gestionnaire de contexte est invoquée.si une cible a été incluse dans la déclaration with, la valeur de retour de
__enter__()
lui est attribuée.la suite est exécutée.
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.
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.
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
.
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()
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.