您可以在首次导入 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 作业中完成工作并执行它。命令行脚本意味着偶尔会出现短暂的内存使用,但作业的启动成本较高,因为每次都需要加载所有内容。