Domanda

Sto lavorando su un demone in cui devo incorporare un server HTTP.Sto tentando di farlo con BaseHTTPServer, che quando lo eseguo in primo piano funziona correttamente, ma quando provo a inserire il demone in background, smette di funzionare.La mia applicazione principale continua a funzionare, ma BaseHTTPServer no.

Credo che questo abbia qualcosa a che fare con il fatto che BaseHTTPServer invia i dati di registro a STDOUT e STDERR.Li sto reindirizzando ai file.Ecco lo snippet 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 impedisce di essere demonizzato?

Modificare:Codice aggiornato per dimostrare il flusso di codice aggiuntivo, precedentemente mancante e che log.debug mostra nel mio demone in background biforcato, sto colpendo il codice dopo il fork.

È stato utile?

Soluzione

Dopo aver cercato un po' su Google, I finalmente sono incappato in questa documentazione di BaseHTTPServer e dopo ho concluso 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 arriva dopo che ho effettuato la fork e ha finito per risolvere il mio problema.

Altri suggerimenti

Ecco come farlo con demone-python biblioteca:

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 al solito per un demone, devi decidere come interagire con il programma dopo che sarà diventato un demone.Ad esempio, potresti registrare un servizio systemd o scrivere un file PID, ecc.Tutto questo però esula dall'ambito della questione.

Si inizia istanziando un HTTPServer.Ma in realtà non gli dici di iniziare a servire in nessuno dei codici forniti.Nel tuo processo figlio prova a chiamare server.serve_forever().

Guarda questo per riferimento

Una soluzione semplice che ha funzionato per me è stata quella di sovrascrivere il file BaseHTTPRequestHandler metodo log_message(), quindi evitiamo qualsiasi tipo di scrittura in stdout ed evitiamo problemi durante la demonizzazione.

class CustomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

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

...
rest of custom class code
...

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

Inoltre, la tua migliore opzione:Non utilizzare BaseHTTPServer.È davvero brutto.Esistono molti buoni server HTTP per Python, ad es. cherrypy O impasto.Entrambi includono script di demonizzazione pronti all'uso.

Dato che questo ha sollecitato risposte da quando ho pubblicato originariamente, ho pensato di condividere alcune piccole 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 problema è creare i propri gestori.Nel caso in cui desideri utilizzare il modulo di registrazione predefinito, puoi fare qualcosa del genere:

# 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 sono passato all'utilizzo del very nice Tornado progetto per i miei server http incorporati.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top