2009-05-20 15 views
9

Sto lavorando su un demone in cui ho bisogno di incorporare un server HTTP. Sto provando a farlo con BaseHTTerver, che quando lo eseguo in primo piano, funziona bene, ma quando provo a lanciare il demone sullo sfondo, smette di funzionare. La mia applicazione principale continua a funzionare, ma BaseHTTerver non lo fa.Daemonizing BaseHTTPServer di python

Credo che questo abbia qualcosa a che fare con il fatto che BaseHTTerver invia i dati di registro a STDOUT e STDERR. Sto reindirizzando quelli ai file. Ecco il frammento di codice:

# Start the HTTP Server 
server = HTTPServer((config['HTTPServer']['listen'],config['HTTPServer']['port']),HTTPHandler) 

# Fork our process to detach if not told to stay in foreground 
if options.foreground is False: 
    try: 
     pid = os.fork() 
     if pid > 0: 
      logging.info('Parent process ending.') 
      sys.exit(0)    
    except OSError, e: 
     sys.stderr.write("Could not fork: %d (%s)\n" % (e.errno, e.strerror)) 
     sys.exit(1) 

    # Second fork to put into daemon mode 
    try: 
     pid = os.fork() 
     if pid > 0: 
      # exit from second parent, print eventual PID before 
      print 'Daemon has started - PID # %d.' % pid 
      logging.info('Child forked as PID # %d' % pid) 
      sys.exit(0) 
    except OSError, e: 
     sys.stderr.write("Could not fork: %d (%s)\n" % (e.errno, e.strerror)) 
     sys.exit(1) 


    logging.debug('After child fork') 

    # Detach from parent environment 
    os.chdir('/') 
    os.setsid() 
    os.umask(0) 

    # Close stdin  
    sys.stdin.close() 

    # Redirect stdout, stderr 
    sys.stdout = open('http_access.log', 'w') 
    sys.stderr = open('http_errors.log', 'w')  

# Main Thread Object for Stats 
threads = [] 

logging.debug('Kicking off threads') 

while ... 
    lots of code here 
... 

server.serve_forever() 

sto facendo qualcosa di sbagliato qui o è BaseHTTPServer in qualche modo impedito di diventare daemonized?

Modifica: codice aggiornato per dimostrare il flusso di codice aggiuntivo, in precedenza mancante e che mostra log.debug nel mio demone biforcuto, background sto colpendo codice dopo fork.

risposta

7

Dopo un po 'di googling ho finally stumbled over this BaseHTTPServer documentation e dopo che ho finito con:

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 
from SocketServer import ThreadingMixIn 

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): 
    """Handle requests in a separate thread.""" 

server = ThreadedHTTPServer((config['HTTPServer']['listen'],config['HTTPServer']['port']), HTTPHandler) 
server.serve_forever() 

che per la maggior parte viene dopo ho forchetta e finito per risolvere il mio problema.

0

Basta usare daemontools o qualche altro script simile invece di eseguire il proprio processo di demonizzazione. È molto meglio non tenerlo fuori dalla sceneggiatura.

Inoltre, l'opzione migliore: non utilizzare BaseHTTerver. È veramente brutto Esistono molti ottimi server HTTP per python, ovvero cherrypy o paste. Entrambi includono script diemonizzazione pronti per l'uso.

+1

Perché è esattamente BAD? per favore, questo è uno dei migliori risultati in google ... Ci sono una massiccia quantità di librerie di python prefabbricate per http quindi una descrizione più dettagliata di quelle sarebbe stupefacente. – nsij22

2

Si avvia istanziando un HTTPServer. Ma in realtà non lo dici per iniziare a servire in uno qualsiasi dei codici forniti. Nel tuo processo figlio prova a chiamare server.serve_forever().

See this per riferimento

+0

In realtà, stavo eseguendo quello dopo il fork, e in base alla mia uscita di debug, dopo il secondo fork, sto colpendo il codice. –

+0

Hai ragione. Ho modificato la mia risposta. – monowerker

4

Ecco come fare questo con la libreria python-daemon:

from BaseHTTPServer import (HTTPServer, BaseHTTPRequestHandler) 
import contextlib 

import daemon 

from my_app_config import config 

# Make the HTTP Server instance. 
server = HTTPServer(
    (config['HTTPServer']['listen'], config['HTTPServer']['port']), 
    BaseHTTPRequestHandler) 

# Make the context manager for becoming a daemon process. 
daemon_context = daemon.DaemonContext() 
daemon_context.files_preserve = [server.fileno()] 

# Become a daemon process. 
with daemon_context: 
    server.serve_forever() 

Come di consueto per un demone, è necessario decidere come si intende interagire con il programma dopo che diventa un demone. Ad esempio, è possibile registrare un servizio systemd o scrivere un file PID, ecc. Tuttavia, tutto ciò esula dall'ambito di applicazione della domanda.

+1

Funziona alla grande per avviare il demone ma come lo utilizzerei con i comandi start e stop? Finora non ero in grado di fermare il demone. – anno1337

+0

Come si ferma questo demone? – MohitC

0

Poiché questo ha sollecitato le risposte da quando ho postato originariamente, ho pensato di condividere alcune informazioni.

Il problema con l'output ha a che fare con il fatto che il gestore predefinito per il modulo di registrazione utilizza StreamHandler. Il modo migliore per gestire questo è creare i tuoi gestori. Nel caso in cui si desidera utilizzare il modulo di registrazione predefinito, si può fare qualcosa di simile:

# Get the default logger 
default_logger = logging.getLogger('') 

# Add the handler 
default_logger.addHandler(myotherhandler) 

# Remove the default stream handler 
for handler in default_logger.handlers: 
    if isinstance(handler, logging.StreamHandler): 
     default_logger.removeHandler(handler) 

Anche a questo punto mi si sono trasferiti a utilizzare il progetto molto bello Tornado per i miei server HTTP incorporato.

1

Una semplice soluzione che ha funzionato per me è stato quello di eseguire l'override del metodo di BaseHTTPRequestHandlerlog_message(), così abbiamo evitare qualsiasi tipo di scrittura in stdout ed evitare problemi quando demonizzare.

class CustomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 

    def log_message(self, format, *args): 
      pass 

... 
rest of custom class code 
...