避免在 parent 进程被杀死时杀死 children

Avoid killing children when parent process is killed

我在基于 flask 的 Web 应用程序中使用库 multiprocessing 来启动 long-running 进程。执行此操作的函数如下:

def execute(self, process_id):
    self.__process_id = process_id
    process_dir = self.__dependencies["process_dir"]
    self.fit_dependencies()
    process = Process(target=self.function_wrapper, name=process_id, args=(self.__parameters, self.__config, process_dir,))
    process.start()

当我想在此 Web 应用程序上部署一些代码时,我重新启动了由 nginx 提供服务的 gunicorn 服务。我的问题是这次重新启动会杀死所有由此应用程序启动的 children 进程,就好像 SIGINT 信号已发送到所有 children。我怎样才能避免这种情况?

编辑: 阅读 this post 后,看来这种行为是正常的。答案建议改用 subprocess 库。所以我重新表述了我的问题:如果我想在 python 脚本中启动 long-running 任务(它们是 python 函数)并确保它们能够在 parent 进程 确保 parent 进程(这是一个 gunicorn 实例)能够在部署中存活下来?

最终编辑: 我选择了@noxdafox 的答案,因为它是更完整的答案。首先,使用进程排队系统可能是这里的最佳实践。然后作为解决方法,我仍然可以使用 multiprocessing 但在函数内部使用 python-daemon 上下文(参见 here ans here)包装纸。最后,@Rippr 建议将 subprocess 与不同的进程组一起使用,这比使用 multiprocessing 分叉更干净,但涉及启动独立函数(在我的如果我从导入的库中启动特定功能)。

我不推荐你的设计,因为它很容易出错。更好的解决方案是 de-couple 服务器的工作人员使用某种排队系统(RabbitMQCeleryRedis、...)。

尽管如此,这里有几个 "hacks" 您可以尝试一下。

  1. 将您的子进程变成 UNIX 守护进程。 python daemon 模块可以作为起点。
  2. 指示您的子进程忽略 SIGINT 信号。如果子进程拒绝终止,服务编排器可能会通过发出 SIGTERMSIGKILL 信号来解决这个问题。您可能需要禁用此类功能。

    为此,只需在 function_wrapper 函数的开头添加以下行:

    signal.signal(signal.SIGINT, signal.SIG_IGN)
    

最终,这个问题归结为误解了部署的含义。在像 Python 这样的 non-enterprise 语言中(与像 Erlang 这样的企业语言相比),人们普遍认为部署会消除之前 运行 过程中的任何人工制品。因此,如果您的旧 children/functions 在执行新部署后实际上没有终止,那显然是一个错误。

唱反调,从你的 question/spec 甚至不清楚你对部署的实际期望是什么——你只是期望你的旧 "functions" 永远 运行 ?这些功能最初是如何启动的?谁应该知道那些 "functions" 在给定部署中是否被修改,以及它们是否应该重新启动以及以何种方式重新启动? Erlang/OTP(与 Python 无关)中对这些问题进行了很多考虑,因此,当你使用一种语言时,你不能简单地期望机器能读懂你的想法像 Python 甚至不是为这样的 use-case.

设计的

因此,将 long-running 逻辑与其余代码分开并适当地执行部署可能是更好的选择。正如另一个答案所提到的,这可能涉及直接从 Python 中生成一个单独的 UNIX daemon,或者甚至可能使用完全独立的逻辑来处理这种情况。

加上@noxdafox 的优秀,我想你可以考虑这个替代方案:

subprocess.Popen(['nohup', 'my_child_process'], preexec_fn=os.setpgrp)

基本上,子进程被杀死是因为它们与父进程属于同一个进程组。通过添加 preexec_fn=os.setpgrp 参数,您只是请求子进程在它们自己的进程组中生成,这意味着它们不会收到终止信号。

解释取自here