Демонизирующий питон BaseHTTPServer - PullRequest
9 голосов
/ 20 мая 2009

Я работаю над демоном, где мне нужно встроить 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 каким-то образом мешает стать демонизированным?

Редактировать: Обновлен код для демонстрации дополнительного, ранее отсутствующего потока кода и того, что log.debug показывает в моем разветвленном фоновом демоне. Я нажимаю код после fork.

Ответы [ 6 ]

7 голосов
/ 21 мая 2009

После небольшого поиска в Google, я наконец наткнулся на эту документацию 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()

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

3 голосов
/ 12 февраля 2010

Вот как это сделать с библиотекой 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 и т. Д. Это все за рамками вопроса.

2 голосов
/ 20 мая 2009

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

См. для справки

1 голос
/ 20 декабря 2011

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

class CustomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

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

...
rest of custom class code
...
0 голосов
/ 13 февраля 2010

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

Проблема с выводом связана с тем, что обработчик по умолчанию для модуля ведения журнала использует 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)

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

0 голосов
/ 20 мая 2009

Просто используйте daemontools или другой подобный скрипт вместо того, чтобы запускать собственный демонизирующий процесс. Гораздо лучше оставить это в своем сценарии.

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

...