Прослушивание Twisted TCP-соединения с python-daemon дает неверный дескриптор файла
Я пытаюсь создать программу, которая:
- вилка в начале использования
multiprocessing
- разветвленный процесс использует
python-daemon
снова раскошелиться на заднем плане - открытие искаженного порта прослушивания TCP в результирующем фоновом процессе
Причина, по которой мне нужно раскошелиться перед запуском python-daemon
потому что я хочу, чтобы процесс запуска оставался в живых (по умолчанию python-daemon
убивает отца процесса).
Пока что мой код:
from twisted.web import xmlrpc, server
from twisted.internet import reactor
from daemon import daemon
import multiprocessing
import os
import logging
class RemotePart(object):
def setup(self):
self.commands = CommandPart()
reactor.listenTCP(9091, server.Site(self.commands))
class CommandPart(xmlrpc.XMLRPC, object):
def __init__(self):
super(CommandPart, self).__init__()
def xmlrpc_stop(self):
return True
class ServerPart(object):
def __init__(self):
self.logger = logging.getLogger("server")
self.logger.info("ServerPart.__init__()")
def start_second_daemon(self):
self.logger.info("start_second_daemon()")
daemon_context = daemon.DaemonContext(detach_process=True)
daemon_context.stdout = open(
name="log.txt",
mode='w+',
buffering=0
)
daemon_context.stderr = open(
name="log.txt",
mode='w+',
buffering=0
)
daemon_context.working_directory = os.getcwd()
daemon_context.open()
self.inside_daemon()
def inside_daemon(self):
self.logger.setLevel(0)
self.logger.info("inside daemon")
self.remote = RemotePart()
self.remote.setup()
reactor.run()
class ClientPart(object):
def __init__(self):
logging.basicConfig(level=0)
self.logger = logging.getLogger("client")
self.logger.info("ClientPart.__init__()")
def start_daemon(self):
self.logger.info("start_daemon()")
start_second_daemon()
def launch_daemon(self):
self.logger.info("launch_daemon()")
server = ServerPart()
p = multiprocessing.Process(target=server.start_second_daemon())
p.start()
p.join()
if __name__ == '__main__':
client = ClientPart()
client.launch_daemon()
Запуск процесса, похоже, работает:
INFO:client:ClientPart.__init__()
INFO:client:launch_daemon()
INFO:server:ServerPart.__init__()
INFO:server:start_second_daemon()
Но, глядя на файл журнала фонового процесса, витая не может открыть порт TCP:
INFO:server:inside daemon
Traceback (most recent call last):
File "forking_test.py", line 74, in <module>
client.launch_daemon()
File "forking_test.py", line 68, in launch_daemon
p = multiprocessing.Process(target=server.start_second_daemon())
File "forking_test.py", line 45, in start_second_daemon
self.inside_daemon()
File "forking_test.py", line 51, in inside_daemon
self.remote.setup()
File "forking_test.py", line 12, in setup
reactor.listenTCP(9091, server.Site(self.commands))
File "/usr/lib/python2.7/site-packages/twisted/internet/posixbase.py", line 482, in listenTCP
p.startListening()
File "/usr/lib/python2.7/site-packages/twisted/internet/tcp.py", line 1004, in startListening
self.startReading()
File "/usr/lib/python2.7/site-packages/twisted/internet/abstract.py", line 429, in startReading
self.reactor.addReader(self)
File "/usr/lib/python2.7/site-packages/twisted/internet/epollreactor.py", line 247, in addReader
EPOLLIN, EPOLLOUT)
File "/usr/lib/python2.7/site-packages/twisted/internet/epollreactor.py", line 233, in _add
self._poller.register(fd, flags)
IOError: [Errno 9] Bad file descriptor
Любая идея? Похоже на то python-daemon
закрывает все файловые дескрипторы фонового процесса, когда начинается этот процесс, возможно, это происходит из-за этого поведения?
1 ответ
Есть много причин, по которым запуск fork, а затем запуск произвольного кода библиотеки не работает. Было бы трудно перечислить их все здесь, но в целом это не круто. Мое предположение относительно того, что конкретно здесь происходит, заключается в том, что что-то внутри multiprocessing
закрывает дескриптор файла "waker", который позволяет Twisted связываться с пулом потоков, но я не совсем уверен.
Если бы вы переписали это:
- использование
spawnProcess
вместоmultiprocessing
- использование
twistd
вместоpython-daemonize
взаимодействия будут гораздо менее удивительными, потому что вы будете использовать код порождения процессов и демонизации, специально разработанный для работы с Twisted, вместо двух вещей с множеством случайных взаимодействий платформы (вызов fork, сериализация вещей по каналам с помощью pickle, вызов settsid и установка и изменение управляющего терминала и лидера сеанса в разное время).
(И на самом деле я бы порекомендовал интегрироваться с инструментами управления демоном вашей платформы, такими как upstart
или же launchd
или же systemd
или кроссплатформенный, как runit
вместо того, чтобы зависеть от любого кода демонизации, в том числе в twistd
, но мне нужно знать больше о вашем приложении, чтобы знать, что рекомендовать.)