如何运行长期(无限)Python 进程?

Posted

技术标签:

【中文标题】如何运行长期(无限)Python 进程?【英文标题】:How do I run long term (infinite) Python processes? 【发布时间】:2012-01-30 22:10:52 【问题描述】:

我最近开始尝试使用 Python 进行 Web 开发。到目前为止,我已经在使用 Apache 与 mod_wsgi 和 Python 2.7 的 Django Web 框架方面取得了一些成功。但是,我遇到了一些问题,比如让进程不断运行、更新信息等。

我编写了一个脚本,我称之为“daemonManager.py”,它可以启动和停止所有或单个 python 更新循环(我应该称它们为守护进程吗?)。它通过分叉,然后为它应该运行的特定功能加载模块并启动无限循环来做到这一点。它在/var/run 中保存一个PID 文件以跟踪进程。到目前为止,一切都很好。我遇到的问题是:

有时其中一个进程会退出。我早上检查ps,这个过程就结束了。没有记录错误(我正在使用logging 模块),并且我涵盖了我能想到的所有异常并记录它们。此外,我认为这些退出进程与我的代码没有任何关系,因为我所有的进程都运行完全不同的代码并以非常相似的间隔退出。我当然可能是错的。 Python 进程在运行数天/数周后就死机是否正常?我应该如何解决这个问题?我是否应该编写另一个守护程序来定期检查其他守护程序是否仍在运行?如果那个守护进程停止了怎么办?我不知道如何处理这个问题。

如何以编程方式知道进程是否仍在运行?我将 PID 文件保存在 /var/run 中并检查 PID 文件是否存在以确定进程是否正在运行。但如果进程因意外原因而死,PID 文件将保留。因此,每次进程崩溃(每周几次)时,我都必须删除这些文件,这有点违背了目的。我想我可以检查一个进程是否在文件中的 PID 上运行,但是如果另一个进程已经启动并被分配了死进程的 PID 怎么办?我的守护进程会认为该进程运行良好,即使它早已死亡。再次,我不知道如何处理这个问题。

关于如何最好运行无限 Python 进程的任何有用答案,希望也能对上述问题有所了解,我会接受


我在 Ubuntu 机器上使用 Apache 2.2.14。 我的 Python 版本是 2.7.2

【问题讨论】:

如果您添加一些代码示例来显示正在崩溃的守护程序的代码,我们或许能够解决具体问题。首先,我会从您的脚本中删除所有处理分叉、监控、重定向等的代码。 您能否澄清您是从在 mod_wsgi 下运行的 WSGI 应用程序还是单独分叉这些守护进程。您不应该从在 mod_wsgi 下运行的应用程序创建此类进程。 听起来这里正在进行大量宣传。我的意思是,这是一个很好的问题,针对特定技术给出了答案,给出了另一个答案,其中再次回答“我也最终使用”了另一种(竞争性?)技术...... 我不关注。这个问题有162个观点,这到底是一种什么样的宣传。我可以向你保证,我对 Forever 印象深刻,可以在下面的评论中推荐它 【参考方案1】:

我假设您正在运行 Unix/Linux,但您并没有真正说出来。我对你的问题没有直接的建议。所以我不希望成为这个问题的“正确”答案。但这里有一些值得探索的地方。

首先,如果您的守护程序崩溃,您应该修复它。只有有错误的程序才会崩溃。也许您应该在调试器下启动它们,看看当它们崩溃时会发生什么(如果可能的话)。您在这些进程中是否有任何跟踪记录?如果没有,请添加它们。这可能有助于诊断您的崩溃。

其次,您的守护进程是提供服务(打开管道并等待请求)还是执行定期清理?如果它们是定期清理过程,您应该使用 cron 定期启动它们,而不是让它们在无限循环中运行。 Cron 进程应该优先于守护进程。同样,如果它们是开放端口和服务请求的服务,您是否考虑过让它们与 INETD 一起使用?同样,单个守护进程 (inetd) 应该优于一堆守护进程。

第三,正如您所发现的,将 PID 保存在文件中并不是很有效。也许共享 IPC,如信号量,会更好地工作。不过我这里没有任何细节。

第四,有时我需要在网站上下文中运行的东西。我使用一个使用维护 URL 调用 wget 的 cron 进程。您设置一个特殊的 cookie 并在 wget 命令行中包含 cookie 信息。如果特殊 cookie 不存在,则返回 403 而不是执行维护过程。这里的另一个好处是登录数据库和避免其他环境问题,因为服务于普通网页的代码正在服务于维护过程。

希望能给你一些想法。我认为尽可能避免使用守护进程是最好的起点。如果您可以在 mod_wsgi 中运行您的 python,那么您不必支持多个“环境”。调试一次运行几天后失败的进程简直是残酷的。

【讨论】:

感谢您的好建议。顺便说一句,我确实指定我正在运行 Ubuntu :) 哦,最后。没看到。【参考方案2】:

我将首先声明这是管理长期运行进程 (LRP) 的一种方法——事实上并非如此。

根据我的经验,最好的产品来自于专注于您正在处理的特定问题,同时将支持技术委派给其他库。在这种情况下,我指的是后台进程(双叉的艺术)、监控和日志重定向的行为。

我最喜欢的解决方案是http://supervisord.org/

使用像 supervisord 这样的系统,您基本上可以编写一个传统的 Python 脚本,该脚本在陷入“无限”循环时执行任务。

#!/usr/bin/python

import sys
import time

def main_loop():
    while 1:
        # do your stuff...
        time.sleep(0.1)

if __name__ == '__main__':
    try:
        main_loop()
    except KeyboardInterrupt:
        print >> sys.stderr, '\nExiting by user request.\n'
        sys.exit(0)

以这种方式编写脚本使开发和调试变得简单方便(您可以在终端中轻松启动/停止它,在事件展开时观察日志输出)。当需要投入生产时,您只需定义一个调用您的脚本的主管配置(这里是定义“程序”的完整示例,其中大部分是可选的:http://supervisord.org/configuration.html#program-x-section-example)。

Supervisor 有一堆的配置选项所以我就不一一列举了,但是我会说它专门解决了你描述的问题:

后台/守护进程 PID 跟踪(可配置为在进程意外终止时重新启动) 在您的脚本中正常登录(如果使用日志模块而不是打印,则为流处理程序),但让主管为您重定向到文件。

【讨论】:

我最终放弃了自己的守护进程解决方案,因为事实证明我在这个主题上没有足够的经验。我还最终使用了forever by nodejitsu,这是一个美味的不需要配置(但可能有大量配置)的应用程序,您只需要指定可执行文件和参数,脚本将永远作为守护进程运行,在崩溃时重新启动。我还通过检查自动输出日志解决了一些长期存在的错误。我接受您的回答,认为这是最接近我的解决方案的回答 在您手动退出脚本后,Supervisor 会重新启动您的脚本吗? @Jakobud 当主管管理退出的进程时(通过sys.exit(),未捕获的异常,或者如果脚本以其他方式到达其结尾 - 也许没有循环?),它将尝试重新启动它。有一些设置可以控制重新启动尝试的次数,以及尝试之间等待的时间。一旦所有的尝试都用完了,它就会放弃。如果要停止正在运行的作业,则应使用 supervisorctl 将其关闭。 主管很棒。在发现主管之前,我在 python-daemon 周围猛撞了一下。【参考方案3】:

假设您的程序、Python 解释器或您正在使用的任何 Python 库/模块中没有任何内存泄漏,您应该认为 Python 进程能够“永远”运行。 (即使面对内存泄漏,如果你在 64 位机器上有足够的交换空间,你也可以永远运行。几十年,如果不是几个世纪,应该是可行的。我让 Python 进程几乎可以正常运行在有限的硬件上两年——在需要移动硬件之前。)

当 Linux 发行版使用 SysV-style init 时,确保程序在死后重新启动非常简单——您只需在 /etc/inittabinit(8) 中添加一个新行,将在启动时生成您的程序并重新生成它如果它死了。 (我知道没有任何机制可以用新的upstartinit-replacement 来复制这个功能,现在许多发行版都在使用它。我并不是说这是不可能的,我只是不知道该怎么做。)

但即使是多年前的init(8) 机制也没有某些人希望的那么灵活。 DJB 的daemontools 包是旨在使守护进程永生的过程控制和监视工具的一个示例。 Linux-HA 套件提供了另一个类似的工具,尽管它可能提供了太多的“额外”功能来证明这项任务的合理性。 monit 是另一种选择。

【讨论】:

以上是关于如何运行长期(无限)Python 进程?的主要内容,如果未能解决你的问题,请参考以下文章

在长期运行的 Python 进程中迭代大型数据集 - 内存问题?

如何从子进程 python 2.7 和 Apache 读取实时输出

如何通过 SSH 启动进程,使其继续运行?

python语法基础-并发编程-进程-长期维护

如何保证服务长期运行?

无限循环中的NodeJS内存消耗