我可以让一些代码像守护进程一样在 Django 中不断运行吗

Posted

技术标签:

【中文标题】我可以让一些代码像守护进程一样在 Django 中不断运行吗【英文标题】:Can I have some code constantly run inside Django like a daemon 【发布时间】:2011-09-25 20:10:02 【问题描述】:

我正在使用 mod_wsgi 通过 Apache 为 django 站点提供服务。我还有一些作为后台进程运行的 Python 代码(dameon?)。它不断轮询服务器并将数据插入其中一个 Django 模型。这工作正常,但我可以让这段代码成为我的 Django 应用程序的一部分,并且能够在后台持续运行吗?它本身不需要是一个过程,而是不断活跃的 Django 站点的艺术。如果是这样,您能否指出一个示例或一些文档来帮助我完成此任务?

谢谢。

【问题讨论】:

【参考方案1】:

您可以设置一个运行您定义的某些功能的 cron 作业,或者 - 更高级且可能推荐的方法,将 celery 集成到您的项目中(实际上这很容易)。

【讨论】:

我和 Celery 一起去了。像魅力一样工作。 cron 任务不会在后台连续运行,而是在给定时间开始并在准备好时结束。由于 django-commands 需要大约 1.5 秒才能启动(取决于模型的复杂性),所以当需要高性能时,这通常不是这样做的方法。 这仍然有效吗?如果是的话,什么是守护 Celery 任务的好方法,可以完成我的长时间处理? 我为什么要使用任何经纪人来做简单的后台任务?配置太复杂了。【参考方案2】:

您可以在首次导入 WSGI 脚本时创建一个后台线程。

import threading
import time

def do_stuff():
    time.sleep(60)
    ... do periodic job

_thread = threading.Thread(target=do_stuff)
_thread.setDaemon(True)
_thread.start()

尽管您必须只使用一个守护进程,但要使其正常工作,否则每个进程都会做同样的事情,而您可能不希望这样做。

如果您在守护进程组中使用多个进程,另一种方法是创建一个特殊的守护进程组,其唯一目的是运行此后台线程。换句话说,该进程实际上并没有收到任何请求。

你可以这样做:

WSGIDaemonProcess django-jobs processes=1 threads=1
WSGIImportScript /usr/local/django/mysite/apache/django.wsgi \
    process-group=django-jobs application-group=%GLOBAL

WSGIImportScript 指令表示加载该脚本并在启动时在进程组“django-jobs”的上下文中运行它。

为了避免拥有多个脚本,我已经指出了您用于 WSGIScriptAlias 的原始 WSGI 脚本文件。我们不希望它在被该指令加载时运行,所以我们这样做:

import mod_wsgi

if mod_wsgi.process_group == 'django-jobs':
    _thread = threading.Thread(target=do_stuff)
    _thread.setDaemon(True)
    _thread.start()

这里它查看守护进程组的名称,并且仅在为此设置了单个进程的特殊守护进程组中启动时运行。

总体而言,您只是将 Apache 用作一个备受赞誉的进程管理器,尽管它已经众所周知是强大的。这有点矫枉过正,因为这个过程会在接受和处理请求的基础上消耗额外的内存,但根据您正在做的事情的复杂性,它仍然很有用。

这样做的一个可爱的方面是,由于它仍然是一个完整的 Django 应用程序,您可以将特定的 URL 映射到这个进程,从而提供一个远程 API 来管理或监视后台任务及其正在做什么。

WSGIDaemonProcess django-jobs processes=1 threads=1
WSGIImportScript /usr/local/django/mysite/apache/django.wsgi \
    process-group=django-jobs application-group=%GLOBAL

WSGIDaemonProcess django-site processes=4 threads=5
WSGIScriptAlias / /usr/local/django/mysite/apache/django.wsgi

WSGIProcessGroup django-site
WSGIApplicationGroup %GLOBAL

<Location /admin>
WSGIProcessGroup django-jobs
</Location>

这里,除了 /admin 下的东西之外的所有 URL 都在 'django-site' 中运行,而 /admin 在 'django-jobs' 中。

无论如何,这解决了根据要求在 Apache mod_wsgi 守护进程中执行此操作的具体问题。

正如所指出的,替代方法是有一个命令行脚本来设置和加载 Django,并从 cron 作业中完成工作并执行它。命令行脚本意味着偶尔会出现短暂的内存使用,但作业的启动成本较高,因为每次都需要加载所有内容。

【讨论】:

如果你正在使用 Gunicorn,你可以通过覆盖 Gunicorn 的 when_ready 函数来启动一个新线程。这是一个示例:github.com/benoitc/gunicorn/blob/master/examples/… 根据记忆,when_ready 在 gunicorn 父进程中运行。在这样的父进程中做长时间运行的东西通常不是一个好主意。该父进程将被派生为工作进程,并且继承由后台线程引起的任何状态都可能会产生影响,即使后台线程本身不会在派生中幸存下来。【参考方案3】:

我之前使用了一个 cron 作业,但我告诉你,你会在一段时间后切换到 celery。

芹菜是要走的路。此外,您可以分配较长的异步进程,从而加快请求/响应时间。

【讨论】:

以上是关于我可以让一些代码像守护进程一样在 Django 中不断运行吗的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Django 运行自己的守护进程?

在 Elastic Beanstalk 上使用 Supervisor 和 Django 将 Celery 作为守护进程运行

将我的 bash 脚本变成守护进程的选项

使用主管将芹菜作为守护进程运行不起作用

如何使用 Django 配置 Celery 守护进程

celery django 守护进程上的多个工作人员和多个队列