BaseHTTPServer Daemonizing в Python

Я работаю над демоном, где мне нужно встроить HTTP-сервер. Я пытаюсь сделать это с помощью BaseHTTPServer, который, когда я запускаю его на переднем плане, работает нормально, но когда я пытаюсь развести демона в фоновом режиме, он перестает работать. Мое основное приложение продолжает работать, но BaseHTTPServer-нет.

Я считаю, что это связано с тем, что BaseHTTPServer отправляет данные журнала в STDOUT и STDERR. Я перенаправляю их в файлы. Вот код фрагмент:

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

Я делаю что-то неправильно здесь или BaseHTTPServer каким-то образом предотвращается от демонизации?

Edit: обновлен код для демонстрации дополнительного, ранее отсутствовавшего потока кода и этого журнала.debug показывает в моем раздвоенном фоновом демоне, я нажимаю код после вилки.

6 ответов


после немного гуглить I наконец-то наткнулся на эту документацию BaseHTTPServer и после этого я закончил с:

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

который по большей части приходит после того, как я вилка и в конечном итоге решить мою проблему.


вот как это сделать с помощью 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()

как обычно для демона, вам нужно решить, как вы будете взаимодействовать с программой после того, как она станет демоном. Например, можно зарегистрировать службу systemd, или написать PID-файл и т. д. Это все выходит за рамки вопроса.


вы начинаете с создания экземпляра HTTPServer. Но вы на самом деле не говорите ему начать служить в любом из предоставленного кода. В вашем дочернем процессе попробуйте вызвать server.serve_forever().

посмотреть этот для справки


простым решением, которое сработало для меня, было переопределить BaseHTTPRequestHandler метод log_message(), поэтому мы предотвращаем любое написание в stdout и избегаем проблем при демонизации.

class CustomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

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

...
rest of custom class code
...

просто использовать сайтов или другой подобный скрипт вместо прокатки свой собственный процесс daemonizing. Гораздо лучше держать это подальше от вашего сценария.

кроме того, ваш лучший вариант: не используйте BaseHTTPServer. Это действительно плохо. Есть много хороших HTTP-серверов для python, т. е. cherrypy или вставить. Оба входят готовые к использованию daemonizing скрипты.


поскольку это запросило ответы, так как я изначально опубликовал, я подумал, что поделюсь небольшой информацией.

проблема с выводом связана с тем, что обработчик по умолчанию для модуля ведения журнала использует StreamHandler. Лучший способ справиться с этим-создать свои собственные обработчики. В случае, если вы хотите использовать модуль ведения журнала по умолчанию, вы можете сделать что-то вроде этого:

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

также в этот момент я перешел к использованию очень хорошего Торнадо проект для моих встроенных http-серверов.