python - 拥有子进程允许 rpc-server 在子进程存活时重新启动

标签 python linux sockets multiprocessing simplexmlrpcserver

场景

我有一个 rpc-server 需要生成持续数天的重要进程 (multiprocessing.Process)。出于安全原因,我不希望这些进程的生存依赖于 rpc-server。因此,我希望服务器能够在进程运行时并能够重新启动

孤立进程

这个问题可以通过以下方式解决(不要将它粘贴到您不想丢失之前工作的地方,它会关闭您的 python session ):

import os
import multiprocessing
import time

def _job(data):
    for _ in range(3):
        print multiprocessing.current_process(), "is working"
        time.sleep(2)
    print multiprocessing.current_process(), "is done"

#My real worker gets a Connection-object as part of a
#multiprocessing.Pipe among other arguments
worker = multiprocessing.Process(target=_job, args=(None,))
worker.daemon = True
worker.start()
os._exit(0)

问题:如果 worker 处于事件状态,则关闭 rpc-server 的套接字

退出主进程似乎无助于或影响套接字问题的关闭。因此,为了说明服务器重新启动的问题,模拟了在第一个服务器关闭后启动具有相同参数的第二个服务器。

以下完美运行:

import SimpleXMLRPCServer
HOST = "127.0.0.1"
PORT = 45212
s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT))
s.server_close()
s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT))
s.server_close()

但是,如果创建了一个 worker,它会引发一个 socket.error 说明套接字已在使用中:

s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT))
worker = multiprocessing.Process(target=_job, args=(None,))
worker.start()
s.server_close()
s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT)) #raises socket.error
worker.join()
s.server_close()

手动关闭服务器套接字确实有效:

import socket
s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT))
worker = multiprocessing.Process(target=_job, args=(None,))
worker.start()
s.socket.shutdown(socket.SHUT_RDWR)
s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT))
worker.join()
s.server_close()

但是这种行为真的让我很担心。我没有以任何方式将套接字传递给工作人员,但它似乎无论如何都捕获了它。

以前也有类似的问题,但它们倾向于将套接字传递给工作人员,这不是这里的目的。如果我通过发送套接字,我可以在 worker 中关闭它并绕过 shutdown hack:

def _job2(notMySocket):
    notMySocket.close()
    for _ in range(3):
        print multiprocessing.current_process(), "is working"
        time.sleep(2)
    print multiprocessing.current_process(), "is done"

s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT))
worker = multiprocessing.Process(target=_job2, args=(s.socket,))
worker.start()
time.sleep(0.1) #Just to be sure worker gets to close socket in time
s.server_close()
s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT)) 
worker.join()
s.server_close()

但是服务器的socket绝对没有理由去访问worker。我有点不喜欢这个解决方案,即使它是迄今为止最好的解决方案。

问题

有没有一种方法可以限制在使用 multiprocessing.Process 时 fork 的内容,以便只复制我想传递给目标的内容,而不是所有打开的套接字和其他内容?

就我而言,要使此代码正常工作:

s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT))
childPipe, parentPipe = multiprocessing.Pipe()
worker = multiprocessing.Process(target=_job, args=(childPipe,))
worker.start()
s.server_close()
s = SimpleXMLRPCServer.SimpleXMLRPCServer((HOST, PORT)) #raises socket.error
worker.join()
s.server_close()

最佳答案

如果您使用的是 Python 2.x,我认为没有任何方法可以避免 Posix 平台上的这种继承。 os.fork 将始终用于创建新进程,这意味着父进程的整个状态将被复制到子进程。您所能做的就是立即关闭 child 的套接字,这就是您已经在做的事情。避免这种继承的唯一方法是在启动服务器之前启动进程。您可以通过尽早启动 Process 然后使用 multiprocessing.Queue 来交付工作项(而不是 args 关键字)来做到这一点argument) 或 multiprocessing.Event 来指示它应该实际开始工作。根据您需要发送给子进程的内容,您的用例可能实际可行,也可能不可行。

但是,如果您使用的是 Python 3.4+(或可以迁移到 3.4+),则可以使用 spawn or forkserver contexts以避免继承套接字。

spawn

The parent process starts a fresh python interpreter process. The child process will only inherit those resources necessary to run the process objects run() method. In particular, unnecessary file descriptors and handles from the parent process will not be inherited. Starting a process using this method is rather slow compared to using fork or forkserver.

Available on Unix and Windows. The default on Windows.

forkserver

When the program starts and selects the forkserver start method, a server process is started. From then on, whenever a new process is needed, the parent process connects to the server and requests that it fork a new process. The fork server process is single threaded so it is safe for it to use os.fork(). No unnecessary resources are inherited.

例子:

def _job2():
    for _ in range(3):
        print multiprocessing.current_process(), "is working"
        time.sleep(2)
    print multiprocessing.current_process(), "is done"

ctx = multiprocessing.get_context('forkserver')
worker = ctx.Process(target=_job2)
worker.start()

关于python - 拥有子进程允许 rpc-server 在子进程存活时重新启动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25714581/

相关文章:

linux - 将 Linux 桌面中的工作区变成永久终端

linux - shell(zsh)中的echo函数和括号的捕获

c++ - Eclipse CDT : Symbol 'cout' , 'map'、 'vector'、 'size_t' 等无法解析

Python ForkingTCPServer 错误

java - 从文本字段获取文本并使用 BufferedReader 对象传递到字符串

python - 使用 python 日期输入

python - 如何通过 Python 访问共享的 Google Drive 文件?

javascript - 是否可以在 Python 中将函数打印为字符串?

javascript - jquery 与 socket.io 错误

Python拖放,获取文件名