2009-05-20 11 views
9

Ich arbeite an einem Daemon, wo ich einen HTTP-Server einbetten muss. Ich versuche es mit BaseHTTPServer, wenn ich es im Vordergrund ausführe, funktioniert es gut, aber wenn ich versuche, den Daemon in den Hintergrund zu verzweigen, hört er auf zu arbeiten. Meine Hauptanwendung funktioniert weiterhin, aber BaseHTTPServer nicht.Daemonizing Python's BaseHTTPServer

Ich glaube, das hat etwas damit zu tun, dass BaseHTTPServer Protokolldaten an STDOUT und STDERR sendet. Ich leite diese zu Dateien um. Hier ist der Code-Schnipsel:

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

Bin ich etwas zu tun, ist hier falsch oder BaseHTTPServer irgendwie verhindert daemonisierte immer?

Edit: Aktualisierter Code, um den zusätzlichen, bisher fehlenden Codefluss zu demonstrieren, und dass log.debug in meinem gegabelten Hintergrunddämon zeigt, dass ich Code nach fork triff.

Antwort

7

Nach ein bisschen googeln ich finally stumbled over this BaseHTTPServer documentation und danach habe ich mit endete:

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

die zum größten Teil kommt, nachdem ich die Gabel und endete mein Problem nach oben lösen.

0

Verwenden Sie einfach daemontools oder ein anderes ähnliches Skript, anstatt Ihren eigenen Daemonisierungsprozess zu starten. Es ist viel besser, dies von Ihrem Skript fernzuhalten.

Auch Ihre beste Option: Verwenden Sie nicht BaseHTTPServer. Es ist wirklich schlimm. Es gibt viele gute HTTP-Server für Python, d.h. cherrypy oder paste. Beide enthalten gebrauchsfertige Daemonisierungsskripte.

+1

Warum genau ist es SCHLECHT? Bitte, dies ist eines der Top-Ergebnisse in Google ... Es gibt eine riesige Menge von vorgefertigten Python-Bibliothek für http, so dass mehr eine Beschreibung dieser wäre auch erstaunlich. – nsij22

2

Sie beginnen mit der Instantiierung eines HTTPS-Servers. Aber Sie sagen es nicht wirklich, in irgendeinem der gelieferten Codes zu dienen. Versuchen Sie in Ihrem Kindprozess, server.serve_forever() aufzurufen.

See this Referenz

+0

Eigentlich habe ich das nach der Gabel ausgeführt, und entsprechend meiner Debug-Ausgabe, nach der zweiten Gabelung, treffe ich Code. –

+0

Sie haben Recht. Ich habe meine Antwort bearbeitet. – monowerker

4

Hier ist, wie dies mit der python-daemon Bibliothek zu tun:

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

Wie üblich für einen Dämon, müssen Sie entscheiden, wie Sie mit dem Programm interagieren, nachdem er ein Dämon wird. Sie könnten beispielsweise einen Systemd-Dienst registrieren oder eine PID-Datei schreiben usw. Das liegt jedoch außerhalb des Bereichs der Frage.

+1

Funktioniert großartig, um den Daemon zu starten, aber wie würde ich es mit Start-und Stoppbefehlen verwenden? Bisher konnte ich den Daemon nicht stoppen. – anno1337

+0

Wie stoppst du diesen Daemon? – MohitC

0

Da dies Antworten seit ich ursprünglich gepostet hatte, dachte ich, dass ich eine kleine Info teilen würde.

Das Problem mit der Ausgabe hat damit zu tun, dass der Standard-Handler für das Protokollierungsmodul den StreamHandler verwendet. Der beste Weg, dies zu umgehen, ist es, eigene Handler zu erstellen. In dem Fall, dass Sie die Standardprotokollierungsmodul verwenden möchten, können Sie etwas tun:

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

Auch an dieser Stelle ich das sehr schön Tornado Projekt zu verwenden für meine Embedded-http-Server verschoben haben.

1

Eine einfache Lösung, die für mich funktionierte, war die BaseHTTPRequestHandler Methode log_message() überschreiben, so dass wir jede Art von Schreiben in stdout verhindern und Probleme bei der Dämonisierung vermeiden.

class CustomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 

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

... 
rest of custom class code 
...