Django框架 - 是否有可订阅的关机事件?

20 投票
2 回答
10546 浏览
提问于 2025-04-17 19:28

我遇到了一个问题,希望能通过在Django中使用某种形式的关闭钩子来解决。

我还是Python/Django开发的新手,为了帮助自己学习,我给自己设定了一个项目,开发一个在浏览器中运行的COMET/反向Ajax风格的聊天网站。这个网站会不断向服务器请求消息。当服务器收到请求时,它会检查是否有等待的消息。如果没有消息,它会尝试获取一个已经被占用的threading.Lock对象的锁。这会导致处理请求的线程被阻塞,直到收到消息并释放之前提到的锁。

我的问题出现在我关闭服务器的时候。目前我使用的是开发服务器(python manage.py runserver)。如果有一个线程因为上面提到的过程而被阻塞,那么服务器就无法关闭。

有没有办法让我在服务器尝试关闭时执行一些代码,以便释放任何等待的线程呢?

我查看过一个类似的问题,但没有找到帮助。有人猜测可能没有这样的事件钩子,但没有明确的答案。我在网上搜索了很多,但没有找到解决办法。

我使用的是Python 2.7和Django 1.5。

非常感谢你能提供的任何帮助。

2 个回答

6

Stu Gla的回答补充:
在Django的主线程中设置信号事件的处理程序,你可以把处理的初始化放在manage.py文件或者你应用的__init__.py文件里,像这样:

#  {project_root}/{your_app}/__init__.py
import os  
import signal  
import sys  
def my_signal_handler(*args):  
    if os.environ.get('RUN_MAIN') == 'true':  
        print('stopped'.upper())  
    sys.exit(0) 

signal.signal(signal.SIGINT, my_signal_handler)  

这样你就可以在不加--noreload的情况下运行服务器了。
检查一下os.environ.get('RUN_MAIN'),这个函数会被调用两次。

20

我不知道有没有一个关闭事件,但你可以自己创建一个。

当用户按下Ctrl-C时,Python进程会收到一个叫做SIGINT的信号。你可以在你的 wsgi.py 文件中设置一个信号处理器,来捕捉这个SIGINT信号,并把它转发给你的程序。

wsgi.py:

...

import signal
signal.signal(signal.SIGINT, my_signal_handler)

你甚至可以把这个信号转发给Django的信号:

my_django_shutdown_signal = django.dispatch.Signal()

def _forward_to_django_shutdown_signal(signal, frame):
    my_django_shutdown_signal.send('system')
    sys.exit(0)   # So runserver does try to exit
signal.signal(signal.SIGINT, _forward_to_django_shutdown_signal)

因为 signal 只能在主线程中调用,所以在使用runserver时这个方法是行不通的。你可以选择用 --noreload 选项来运行runserver,或者换用gunicorn。

撰写回答