4

设想

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

孤立进程

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

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

但是,如果创建了一个工人,它会提出一个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()

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

之前发布过类似的问题,但它们倾向于将套接字传递给工作人员,此处不打算这样做。如果我通过发送套接字,我可以在工作人员中关闭它并绕过shutdown黑客:

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以便只复制我想传递给目标的内容,而不是所有打开的套接字和其他内容?

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

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

1 回答 1

1

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

但是,如果您使用的是 Python 3.4+(或可以迁移到 3.4+),则可以使用spawnorforkserver上下文来避免继承套接字。

产卵

父进程启动一个新的 python 解释器进程。子进程将仅继承运行进程对象 run() 方法所需的那些资源。特别是,不会继承父进程中不必要的文件描述符和句柄。与使用 fork 或 forkserver 相比,使用这种方法启动进程相当慢。

在 Unix 和 Windows 上可用。Windows 上的默认设置。

分叉服务器

当程序启动并选择forkserver启动方式时,启动一个服务器进程。从那时起,每当需要一个新进程时,父进程都会连接到服务器并请求它派生一个新进程。fork 服务器进程是单线程的,因此使用 os.fork() 是安全的。没有不必要的资源被继承。

例子:

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()
于 2014-09-07T22:31:48.840 回答