我能在Django中像守护进程一样持续运行一些代码吗?

16
我正在使用mod_wsgi通过Apache来提供Django网站的服务。我还有一些Python代码作为后台进程运行(守护进程?)。它不断轮询服务器并将数据插入到Django模型中。这样做很好,但我能否让这段代码成为我的Django应用程序的一部分,并且仍然能够持续在后台运行?它不需要是一个进程,而是一个始终活动的Django站点的一部分。如果可以的话,您能否向我指出一个示例或一些文档,以帮助我完成这个任务?
谢谢。
3个回答

14

您可以设置一个定时任务来运行您定义的函数,或者 - 更高级且可能更推荐的方法是,在您的项目中集成celery(实际上非常简单)。


我选择了Celery。效果很好。 - Mridang Agarwalla
一个 cron 任务并不会一直在后台运行,而是在指定的时间启动,并在准备就绪时结束。由于 Django 命令需要大约1.5秒来启动(取决于模型的复杂性),因此当需要高性能时,通常不应该这样做。 - Alex
这还有效吗?如果是,有没有一个很好的方式来将执行长时间处理的Celery任务变成守护进程? - Divij Sehgal
1
为什么我要使用任何代理来执行简单的后台任务?这太复杂了。 - nllsdfx

11

当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下的内容运行在“django-jobs”中,其他所有URL都在“django-site”中运行。

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

正如所指出的,另一种方法是使用命令行脚本设置和加载Django并执行工作,然后从cron作业中执行该脚本。命令行脚本意味着偶尔会有瞬时内存使用,但作业的启动成本更高,因为每次需要加载所有内容。


如果您正在使用Gunicorn,可以通过覆盖Gunicorn的when_ready函数来启动新线程。以下是一个示例:https://github.com/benoitc/gunicorn/blob/master/examples/example_config.py - Mridang Agarwalla
从内存中来看,“when_ready”在gunicorn父进程中运行。通常不建议在这样的父进程中执行长时间运行的操作。该父进程将被分叉成工作进程,并且继承由后台线程引起的任何状态可能会产生影响,即使后台线程本身不会在分叉后继续存在。 - Graham Dumpleton

0

我之前使用过cron job,但是我告诉你,过一段时间你会转向celery。

Celery是正确的选择。此外,您可以将长时间的异步进程分配任务,以便加快请求/响应时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接