python3 library example daemonize python daemon python-daemon

library - ¿Cómo configuro un daemon con python-daemon?



python daemonize (6)

Soy nuevo en los demonios, así que pido disculpas si esta es una pregunta de novato.

En otras respuestas (por ejemplo, esta pregunta ), las personas sugirieron que el paquete python-daemon era el camino a seguir porque implementa completamente el estándar PEP 3143 .

Desafortunadamente, python-daemon es un poco ligero en la documentación (o más probablemente soy un poco ligero en conocimiento / experiencia ...;)), y creo que probablemente me esté perdiendo algo realmente básico. Esto es lo que estoy haciendo:

Tengo los siguientes

import daemon logfile = open(''daemon.log'', ''w'') context = daemon.DaemonContext(stdout = logfile, stderr = logfile) context.open() with context: do_something_1() do_something_2()

Pregunta: ¿Cómo configuro un daemon con python-daemon, cómo puedo iniciarlo y detenerlo?

Notas al margen:

Básicamente, estoy adivinando cómo o si se debe usar el método .open() aquí: los documentos no fueron muy claros en este punto. Lo mismo parece suceder ya sea que lo incluya o no.

Entonces, ¿qué hago ahora? Cuando intento ejecutar este archivo, por ejemplo:

python startConsumerDaemons.py

parece que se ejecuta do_something_1() , pero no el segundo. Y, parece que deja el programa adjunto a la ventana del terminal. IE, stdout no se redirige, y cuando cierro la ventana de la terminal, el proceso se detiene. Entonces, estoy bastante seguro de que estoy haciendo algo mal aquí ... ¿qué debería hacer de manera diferente?

Y, por último, una vez que inicie el daemon, ¿cómo lo detengo / reinicio (por ejemplo, si hago cambios en el código subyacente)?


Como puede ver en la documentación de la declaración ''con'' , esta declaración realiza algo de ''magia'', que está relacionado con nuestro propósito. Específicamente:

La ejecución de la instrucción with con un "elemento" se realiza de la siguiente manera:

  1. La expresión de contexto (la expresión dada en with_item) se evalúa para obtener un administrador de contexto.

  2. El __exit__() del administrador de contexto se carga para su uso posterior.

  3. Se invoca el método __enter__() del administrador de contexto.

  4. Si se incluyó un objetivo en la declaración with, se le asigna el valor de retorno de __enter__() .

  5. La suite está ejecutada.

  6. Se invoca el método __exit__() del administrador de contexto. Si una excepción causó la salida del conjunto, su tipo, valor y rastreo se pasan como argumentos a __exit__() . De lo contrario, se proporcionan tres argumentos Ninguno.

¿Qué significa esto? Si te fijas bien en el PEP en cuestión , que también sirve como documentación de python-daemon (y que de hecho podría mejorar mucho), verás que implementa __enter__() y __exit__() :

La clase también implementa el protocolo del administrador de contexto a través de los métodos __enter__ y __exit__ .

__enter__()

Llame al método open () de la instancia, luego devuelva la instancia.

__exit__(exc_type, exc_value, exc_traceback)

Llame al método close () de la instancia, luego devuelva True si la excepción fue manejada o False si no lo fue.

En otras palabras, no se necesita open (), el ejemplo dado en el PEP (aunque no se explica correctamente) funciona como está. Mientras que la declaración with significa algo, no mantiene ningún bucle, una vez que se alcanza el final de su alcance, llama a exit (), que en python-daemon significa close (). Por lo tanto, debe poner allí un tiempo Verdadero o cualquier bucle infinito que considere.

En cuanto a que tu segundo guión no funciona, no puedo decirte realmente, me sorprende que el primero ya funcione. Si su demonio se está deteniendo, seguro que hay un problema con sus scripts, puede verificar su consumerDaemonLogFile. (como nota al margen, tiene un error tipográfico ''sderr'' -> ''stderr'')

Además, puede ver en el PEP que, si no se especifica, la propiedad del directorio de trabajo se establece de forma predeterminada en ''/''. Esta podría ser la fuente de su problema si está utilizando rutas relativas en sus scripts.

Finalmente, sobre la última pregunta, puedes matar fácilmente a tu demonio encontrando su PID:

ps ax | grep startConsumerDaemons.py

y enviándole un SIGTERM:

kill <pid>

La respuesta proporcionada por gromain proporciona una manera más práctica de iniciarlo y detenerlo, con ''daemon.runner ()'', pero es mucho más complicado de configurar.


El constructor daemon.DaemonContext acepta un parámetro de daemon.DaemonContext lockfile . Utilice una biblioteca de archivos de bloqueo que registrará el PID del proceso, como lockfile.PIDLockFile .

Luego, el PID del proceso se encuentra simplemente leyendo el contenido del archivo PID nombrado. Utilice ese PID para enviar señales a su demonio en ejecución.


En Linux, puedes detener el Daemon ejecutando:

$ ps -x

y encuentre el PID que corresponde a su demonio y luego simplemente elimine el proceso.


Esto es lo que tengo, que funciona para mí. También tiene un script de inicio sysv. Repo está en GitHub , y también tengo una breve publicación en el blog con enlaces a otras posibles soluciones que encontré.

Solo puede haber un proceso de daemon en ejecución: este es administrado por el archivo de bloqueo PID, como la mayoría de los otros demonios de Linux. Para detenerlo, hacer

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

Para ver si se está ejecutando:

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

Usando el submódulo pidfile, el archivo PID se gestiona automáticamente. Cuando se detiene el demonio, se borra el archivo pid. Por favor, vea el repositorio de GitHub vinculado para el script de inicio.

Aquí está el código del demonio de 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)

Para completar, aquí está el script de inicio. Tenga en cuenta que "matar" es en realidad solo un método para enviar una señal POSIX. El contexto python-daemon capturará la señal, finalizará el proceso cerrando limpiamente los descriptores de archivos y eliminará el archivo PID automáticamente. Por lo tanto, realmente es una terminación limpia.

Puedes escribir tu código para capturar SIGUSR1 o algo similar, para hacer una recarga de la configuración del daemon. No hay ninguna ventaja al escribir Python para detener el demonio.

#!/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 $4}'' ) 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 "$1" 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


Falta una documentación útil para el módulo "daemon de python". Personalmente renuncié a usarlo, y ahora uso con éxito el código daemon de Sander Marechal al que se hace referencia en esta respuesta .

Lo modifiqué ligeramente para poder hacer cosas cuando llamas a python testdaemon.py stop .

Aquí está el código .

Uso de la muestra:

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()


Un ejemplo completo está disponible aquí .

Debería poder comprender mejor el funcionamiento interno de python-daemon.

Además, el código proporcionado también proporciona un ejemplo de un script de inicio para simplemente iniciar / detener el daemon. Sin embargo, puede iniciarlo / detenerlo simplemente llamando de nuevo a la función original con el argumento detener:

python original_func.py stop