2012-10-28 12 views
26

Sono nuovo ai daemons, quindi mi scuso se questa è una domanda per principianti.Come si configura un demone con python-daemon?

In molti altri risposte (per esempio, this question) la gente ha suggerito il pacchetto python-daemon era la strada da percorrere perché implementa pienamente lo standard PEP 3143.

Purtroppo, python-daemon è a bit light on documentation (o più probabilmente sono un po 'leggero sulla conoscenza/esperienza ...;)), e penso che probabilmente manchi qualcosa di veramente semplice. Ecco quello che sto facendo:

Ho il seguente:

import daemon 

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

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

context.open() 

with context: 
    do_something_1() 
    do_something_2() 

Domanda: Come faccio a impostare un demone con python-daemon, come posso avviarlo e fermarlo?


note collaterali:

praticamente sto prendendo un ipotesi su come/se il metodo .open() deve essere utilizzato qui - documenti non erano reali chiaro su questo punto. La stessa cosa sembra accadere sia che la includa o no.

Quindi, ora cosa devo fare? Quando cerco di eseguire questo file, ad esempio:

python startConsumerDaemons.py 

sembra correre do_something_1(), ma non il secondo. E sembra lasciare il programma allegato alla finestra del terminale. IE, lo stdout non viene reindirizzato e quando chiudo la finestra del terminale il processo viene ucciso. Quindi, sono abbastanza sicuro che sto facendo qualcosa di sbagliato qui ... cosa dovrei fare in modo diverso?

E, infine, una volta eseguito il daemon, come posso arrestarlo/riavviarlo (ad esempio se apporto modifiche al codice sottostante)?

+0

Qual è l'ultimo 'with' dovuto fare?(i moduli verranno eseguiti quando vengono importati per la prima volta, a meno che manchi qualcosa, quelle ultime istruzioni non faranno nulla) E cosa intendi con "esegui il primo script ma non il secondo"? Se non è stata sollevata alcuna eccezione, sono abbastanza fiducioso che entrambi sono stati eseguiti con successo. – mgibsonbr

+0

Non so cosa intendi per "ultimo" con (ce n'è solo uno), ma nel codice di esempio per python-daemon, mette lo script da eseguire come daemon all'interno di una dichiarazione simile (http: // www.python.org/dev/peps/pep-3143/). Il primo script viene eseguito ed è in grado di ricevere/elaborare messaggi (è un consumatore su un broker MQ). Perché il primo è su un loop infinito, forse lo script startConsumerDaemons non arriva mai al secondo? – CQP

+0

L'esempio che hai postato ** importa ** alcuni metodi da un altro file ('initial_program_setup',' do_main_program', ecc.) E ** li chiama ** ('do_main_program()'), uno di questi all'interno dell'istruzione 'with' . A meno che non ci sia qualche caratteristica oscura di Python che non conosco, sono sicuro che quelle dichiarazioni non faranno nulla. Ad ogni modo, per quanto ho potuto capire, 'python-daemon' trasforma il programma attualmente in esecuzione in un processo daemon UNIX, non crea nuovi processi o nuovi thread. Se una parte del programma entra nel ciclo infinito, le parti successive non verranno eseguite affatto. – mgibsonbr

risposta

0

Su Linux, è possibile interrompere il Daemon eseguendo:

$ ps -x 

e trovare il PID che corrisponde al demone e poi basta uccidere il processo.

4

Un esempio completo è available here.

Dovresti essere in grado di comprendere meglio il funzionamento interno di python-daemon.

Inoltre, il codice fornito fornisce anche un esempio di uno script di init per avviare/arrestare semplicemente il daemon. Tuttavia, è possibile avviare/fermare semplicemente richiamando di nuovo la funzione originale con la fermata argomento:

python original_func.py stop 
2

Come si può vedere nel 'with' statement documentation, è affermazione non eseguire alcune 'magico', che è legato al nostro scopo .In particolare:

L'esecuzione dell'istruzione with con uno procede “voce” come segue:

  1. L'espressione contesto (l'espressione data nella with_item) viene valutato per ottenere un manager contesto.

  2. Il gestore del contesto __exit__() viene caricato per un uso successivo.

  3. viene richiamato il metodo __enter__() del gestore contesto.

  4. Se una destinazione è stata inclusa nell'istruzione with, viene assegnato il valore di ritorno da __enter__().

  5. La suite viene eseguita.

  6. Viene richiamato il metodo __exit__() del gestore di contesto. Se un'eccezione ha causato l'uscita dalla suite, il suo tipo, il suo valore e il traceback vengono passati come argomenti a __exit__(). In caso contrario, vengono forniti tre argomenti Nessuno .

Cosa significa? Se si guarda da vicino a the PEP in question, che serve come documentazione python-demone come bene (e che anzi potrebbe essere molto migliorata), vedrai che implementa __enter__() e __exit__():

La classe implementa anche il manager contesto protocollo via __enter__ e __exit__ metodi.

__enter__()

chiamata metodo aperto dell'istanza(), per poi tornare l'istanza.

__exit__(exc_type, exc_value, exc_traceback)

chiamare il metodo close() dell'istanza, per poi tornare True se l'eccezione è stata gestita o False se non fosse.

In altre parole, open() non è necessario, l'esempio fornito in PEP (sebbene non spiegato correttamente) funziona così com'è. Mentre l'istruzione with significa qualcosa, non mantiene alcun ciclo, una volta raggiunta la fine del suo ambito, chiama exit(), che in python-daemon significa close(). Pertanto, è necessario mettere un po 'di tempo vero o quale ciclo infinito consideri.

Sul comportamento del tuo secondo script non funziona, non posso davvero dirti, sono sorpreso che il primo funziona già. Se il tuo demone si sta fermando, c'è sicuramente un problema con gli script, puoi controllare il tuo consumerDaemonLogFile. (come nota a margine, si ha un typo 'sderr' -> 'stderr')

Inoltre, è possibile vedere nel PEP che, se non specificato, la proprietà della directory di lavoro è impostata su "/". questa potrebbe essere la fonte del tuo problema se stai usando percorsi relativi negli script.

Infine, l'ultima domanda, si può facilmente uccidere si sta trovando il suo demone PID:

ps ax | grep startConsumerDaemons.py 

e l'invio di un SIGTERM:

kill <pid> 

La risposta fornita da gromain FORNISCE un modo più pratico per avviarlo e fermarlo, con 'daemon.runner()', ma è molto più complicato da configurare.

-1

Il costruttore daemon.DaemonContext accetta un parametro lockfile. Utilizzare una libreria lockfile che registra il PID del processo, ad esempio lockfile.PIDLockFile.

Quindi, il PID del processo viene rilevato semplicemente leggendo il contenuto del file PID denominato. Usa quel PID per inviare segnali al demone in esecuzione.

6

Ecco quello che ho, che funziona per me. Ha anche uno script di init sysv. Repo is at GitHub e ho anche a brief blog post con collegamenti ad altre possibili soluzioni che ho trovato.

È possibile eseguire solo un processo daemon, gestito dal file di blocco PID, come la maggior parte degli altri daemon di Linux. Per fermarlo, fare

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

per vedere se è in esecuzione:

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

Utilizzando il modulo pidfile, il file PID è gestito automaticamente. Quando il daemon viene arrestato, il file di pid viene cancellato. Si prega di consultare il repository GitHub collegato per lo script di init.

Ecco il codice Python daemon:

#!/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) 

Per completezza, qui è lo script di init. Nota che "kill" è in realtà solo un metodo per inviare un segnale POSIX - vedi man page per signal (7) per una panoramica. Il contesto python-daemon catturerà il segnale, terminerà il processo chiudendo in modo pulito i descrittori di file ed eliminerà automaticamente il file PID. Quindi, è davvero una terminazione pulita.

È possibile scrivere il codice per catturare SIGUSR1 o qualcosa di simile, per eseguire un ricaricamento della configurazione del daemon. Non c'è alcun vantaggio nello scrivere Python per fermare il demone.

#!/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 
+0

Bene, il metodo 'stop' con' kill' non è molto bello ... Non è possibile farlo con 'python mydaemon.py stop', ecc.? Inoltre potremmo semplificare il tuo esempio per mantenere solo il nocciolo della questione su start/stop daemon ecc. – Basj

+0

È un modo standard Linux per gestire i daemon. Non c'è processo interattivo. L'unico modo per comunicare sarebbe usare i segnali di Linux. Usare un metodo Python significherebbe concludere una chiamata per uccidere. –

0

Una documentazione utile è ancora mancante per il modulo "python-daemon". Personalmente ho rinunciato a usarlo, e ora uso con successo il daemon di Sander Marechal codice referenced in this answer.

L'ho leggermente modificato per poter fare le cose quando si chiama python testdaemon.py stop.

Here is the code.


utilizzo Esempio:

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