Question

Je travaille sur un démon où je dois intégrer un serveur HTTP. Je tente de le faire avec BaseHTTPServer, qui quand je le lance au premier plan, il fonctionne très bien, mais quand je tente de FORK le démon dans l'arrière-plan, il cesse de fonctionner. Ma principale application continue de travailler, mais BaseHTTPServer ne fonctionne pas.

Je crois que cela a quelque chose à voir avec le fait que BaseHTTPServer envoie des données de journal à stdout et stderr. Je redirigeant les fichiers. Voici l'extrait de code:

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

Ai-je fait quelque chose de mal ici ou est en quelque sorte BaseHTTPServer empêchés d'être daemon?

Edit: code mis à jour pour démontrer le flux de code supplémentaire, manquant précédemment et que log.debug montre dans mon fourchue, démon fond je frappais code après fourchette

.
Était-ce utile?

La solution

Après un peu de googler je finalement trébuché sur cette documentation BaseHTTPServer et après que je me suis retrouvé avec:

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

Ce qui, pour la plupart vient après que je fourchette et a fini par résoudre mon problème.

Autres conseils

Voici comment faire avec le python-démon bibliothèque :

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

Comme d'habitude pour un démon, vous devez décider comment vous allez interagir avec le programme après il devient un démon. Par exemple, vous pouvez enregistrer un service systemd ou écrire un fichier PID, etc. C'est tout en dehors de la portée de la question cependant.

Vous commencez par instancier un HTTPServer. Mais vous ne dites pas réellement de commencer à servir dans tout le code fourni. Dans votre processus d'enfant essayer d'appeler server.serve_forever().

Voir cette référence

Une solution simple qui a fonctionné pour moi était de passer outre la méthode BaseHTTPRequestHandler log_message(), donc nous évitons tout type d'écriture dans stdout et d'éviter des problèmes lors de la diabolisation.

class CustomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

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

...
rest of custom class code
...

Il suffit d'utiliser daemontools ou un autre script similaire au lieu de rouler votre propre processus de daemonizing. Il est préférable de garder ce votre script.

En outre, votre meilleure option: Ne pas utiliser BaseHTTPServer. Il est vraiment mauvais. Il y a beaucoup de bons serveurs HTTP pour python, à savoir cherrypy ou pâte . Les deux comprennent des scripts daemonizing prêts à utiliser.

Depuis ce qui a des réponses sollicité depuis que je l'origine posté, je pensais que je partagerais un peu d'information.

Le problème avec la sortie doit faire avec le fait que le gestionnaire de défaut pour le module de journalisation utilise le StreamHandler. La meilleure façon de gérer cela est de créer vos propres gestionnaires. Dans le cas où vous souhaitez utiliser le module d'enregistrement par défaut, vous pouvez faire quelque chose comme ceci:

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

En outre, à ce stade, je suis déplacé à l'aide de la très belle Tornado pour mon embarqué http serveurs.

scroll top