在父进程被杀死时避免杀害儿童

时间:2018-03-18 17:52:27

标签: python nginx flask gunicorn python-multiprocessing

我在基于 flask 的Web应用程序中使用库多处理来启动长时间运行的进程。执行此操作的功能如下:

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应用程序上部署一些代码时,我重新启动了一个重新启动 gunicorn 的服务,由 nginx 提供服务。我的问题是,这次重启会杀死此应用程序启动的所有子进程,就像向所有子进程发送 SIGINT 信号一样。我怎么能避免这个?

编辑:阅读this post后,似乎此行为正常。答案建议使用子进程库。所以我重新提出我的问题:如果我想在python脚本中启动长时间运行的任务(python函数),并确保它们能够在父进程中存活,那么我该怎么办? OR 确保父进程进程(这是一个枪支实例)将在部署中存活下来?

最终编辑:我选择@noxdafox答案,因为它更完整。首先,使用进程排队系统可能是最佳实践。然后作为一种解决方法,我仍然可以使用多处理,但在函数包装器中使用 python-daemon 上下文(请参阅here ans here)。最后,@ Rippr建议将子进程与不同的进程组一起使用,这比使用多处理分叉更简洁,但需要启动独立的函数(在我的情况下,我从进口图书馆)。

3 个答案:

答案 0 :(得分:3)

我建议不要使用你的设计,因为它很容易出错。更好的解决方案是使用某种排队系统(RabbitMQCeleryRedis,...)将工作人员与服务器分离。

然而,这里有一些你可以尝试的“黑客”。

  1. 将您的子进程转换为UNIX守护进程。 python daemon模块可以作为起点。
  2. 指示您的子进程忽略SIGINT信号。如果子进程拒绝死亡,服务协调器可以通过发出SIGTERMSIGKILL信号来解决此问题。您可能需要禁用此功能。

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

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

答案 1 :(得分:1)

最终,这个问题归结为误解了部署的意义。在像Python这样的非企业语言中(与像Erlang这样的企业语言相比),通常可以理解,部署会消除运行该过程的任何前述假象。因此,如果您的旧子项/函数在执行新部署后实际上没有终止,那么这显然是一个错误。

要扮演魔鬼的拥护者,你的问题/规范甚至还不清楚你对部署的实际期望是什么 - 你只是期望你的旧功能"要永远奔跑吗?这些功能如何首先开始?谁应该知道这些"是否起作用"在给定的部署中进行了修改,以及它们是否应该重新启动以及以何种方式重新启动?在Erlang / OTP(与Python无关)中给出了很多关于这些问题的考虑因素,因此,当你使用像Python这样的语言时,你不能简单地期望机器能够读懂你的思想。 39;甚至不是为这样的用例而设计的。

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

答案 2 :(得分:1)

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

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

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

来自here的解释。