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

41 投票
3 回答
41114 浏览
提问于 2025-04-17 09:18

我最近开始尝试用Python进行网页开发。到目前为止,我用Apache和mod_wsgi,以及Python 2.7的Django框架,取得了一些成功。不过,我遇到了一些问题,就是有些进程需要一直运行,不断更新信息等等。

我写了一个脚本,叫做“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。

3 个回答

2

我猜你是在使用Unix/Linux系统,但你并没有明确说明。我对你的问题没有直接的建议,所以我不指望自己能给出“正确”的答案。不过这里有一些可以探讨的地方。

首先,如果你的守护进程(daemon)崩溃了,你应该先解决这个问题。只有有bug的程序才会崩溃。也许你可以在调试器下启动它们,看看崩溃时发生了什么(如果可以的话)。这些进程有没有记录日志?如果没有,建议加上日志记录。这可能有助于你找出崩溃的原因。

其次,你的守护进程是在提供服务(比如打开管道等待请求)还是在定期清理?如果是定期清理的进程,建议使用cron来定期启动它们,而不是让它们在无限循环中运行。使用cron进程比守护进程更好。同样,如果它们是打开端口并处理请求的服务,你有没有考虑让它们与INETD一起工作?总之,使用一个守护进程(inetd)比一堆守护进程要好。

第三,把进程ID(PID)保存在文件里并不是很有效,正如你所发现的。也许使用共享的进程间通信(IPC),比如信号量,会更好。不过我这里没有具体的细节。

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

希望这些能给你一些启发。我觉得如果可以的话,尽量避免使用守护进程是个不错的开始。如果你能在mod_wsgi中运行你的Python代码,那就省去了支持多个“环境”的麻烦。调试一个运行了几天后崩溃的进程真是太痛苦了。

3

你可以把Python进程想象成可以“永远”运行的,只要你的程序、Python解释器或者你使用的Python库没有内存泄漏的问题。(即使有内存泄漏,如果你的64位机器有足够的交换空间,进程也可能会一直运行下去。几十年,甚至几百年都是有可能的。我有过Python进程在有限的硬件上运行了将近两年,直到硬件需要搬迁。)

确保程序在崩溃后能自动重启,这在以前的Linux系统中非常简单。当时使用的是SysV风格的 init,你只需要在/etc/inittab中添加一行,init(8)就会在启动时启动你的程序,并在它崩溃时重新启动它。(我不知道现在很多发行版使用的新upstart init替代品是否有类似的功能。我不是说这不可能,只是我不知道怎么做。)

不过,即使是以前的init(8)机制也没有那么灵活,很多人对此并不满意。比如,DJB的daemontools包就是一种用于进程控制和监控的工具,旨在让守护进程永远存活。还有Linux-HA套件提供了类似的工具,尽管它可能提供了过多的“额外”功能,不一定适合这个任务。monit也是一个不错的选择。

40

我先说一下,这只是一种管理长时间运行过程(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)

这样写脚本使得开发和调试变得简单方便(你可以在终端轻松地启动/停止它,并在事件发生时查看日志输出)。当你准备把它投入生产时,只需定义一个 supervisor 配置来调用你的脚本(这里有一个完整的“程序”定义示例,大部分内容是可选的: http://supervisord.org/configuration.html#program-x-section-example)。

Supervisor 有很多配置选项,我就不一一列举了,但我可以说它特别解决了你提到的问题:

  • 后台运行/守护进程
  • 进程ID跟踪(可以配置为在进程意外终止时自动重启)
  • 在你的脚本中正常记录日志(如果使用日志模块而不是打印,可以使用流处理器),但让 supervisor 为你重定向到文件。

撰写回答