【问题标题】:Having child-processes allow rpc-server to restart while children survive拥有子进程允许 rpc-server 在子进程存活时重新启动
【发布时间】:2014-09-07 20:40:39
【问题描述】:

场景

我有一个 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()

但是,如果创建了一个 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()

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

之前发布过类似的问题,但他们倾向于将套接字传递给工作人员,此处不打算这样做。如果我通过发送套接字,我可以在工作人员中关闭它并绕过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 时分叉的内容,以便只复制我想传递给目标的内容,而不是所有打开的套接字和其他内容?

在我的例子中,要让这段代码正常工作:

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 linux sockets multiprocessing simplexmlrpcserver


    【解决方案1】:

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

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

    生成

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

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

    forkserver

    程序启动时选择forkserver启动 方法,启动服务器进程。从此,每当有新 需要进程,父进程连接到服务器和 请求它派生一个新进程。 fork server进程是单一的 线程,因此使用 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()
    

    【讨论】:

    • 如果只是移植不会意味着这么多工作,forkserver 解决方案真的很棒。我可能会在一两天内接受这个解决方案,尽管这些建议都不适合我复杂的实际场景和时间限制。
    猜你喜欢
    • 2015-08-17
    • 1970-01-01
    • 1970-01-01
    • 2014-10-11
    • 1970-01-01
    • 1970-01-01
    • 2017-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多