Демонизирующий базовый сервер pythonhttpserver
-
23-08-2019 - |
Вопрос
Я работаю над демоном, в который мне нужно встроить HTTP-сервер.Я пытаюсь сделать это с помощью BaseHTTPServer, который, когда я запускаю его на переднем плане, работает нормально, но когда я пытаюсь перевести демон в фоновый режим, он перестает работать.Мое основное приложение продолжает работать, но BaseHTTPServer этого не делает.
Я полагаю, что это как-то связано с тем фактом, что BaseHTTPServer отправляет данные журнала в STDOUT и STDERR.Я перенаправляю их в files.Вот фрагмент кода:
# 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 в моем разветвленном фоновом демоне, я нажимаю на код после разветвления.
Решение
После небольшого поиска в 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()
Что по большей части происходит после того, как я разветвляюсь и в конечном итоге решаю свою проблему.
Другие советы
Вот как это сделать с помощью python-демон библиотека:
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()
, таким образом, мы предотвращаем любые записи в стандартном режиме и избегаем проблем при демонизации.
class CustomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def log_message(self, format, *args):
pass
...
rest of custom class code
...
Просто используй демонические инструменты или какой-нибудь другой подобный скрипт вместо того, чтобы запускать свой собственный процесс демонизации.Гораздо лучше не включать это в свой сценарий.
Кроме того, ваш лучший вариант:Не используйте BaseHTTPServer.Это действительно плохо.Существует много хороших HTTP-серверов для python, т. е. вишневый или паста.Оба включают готовые к использованию сценарии демонизации.
Поскольку с момента моей первоначальной публикации на это требовались ответы, я подумал, что поделюсь небольшой информацией.
Проблема с выводом связана с тем фактом, что обработчик по умолчанию для модуля ведения журнала использует 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-серверов.