为什么Django的"request_finished"信号会阻塞响应?

2 投票
2 回答
1686 浏览
提问于 2025-04-30 19:49

如果我从一个网站发起一个ajax请求到Django的视图,我希望它能返回一些响应,并且发送一个信号,表示这个响应已经完成。我可能会用类似下面的代码(这里的sleep只是为了演示,实际上可以是任何需要较长时间处理的过程)。

from time import sleep

@receiver(request_finished)
def comment_added(sender, **kwargs):
    sleep(5)
    return

在这个过程中,AJAX的响应会被“sleep”阻塞,直到“sleep”结束,这样用户的体验就会变得很慢。这是不是在某种程度上违背了使用信号的初衷呢?

我知道我可以使用Celery来处理这个问题,我也打算这么做。但我想更好地理解信号的用法。谢谢!

暂无标签

2 个回答

0

Django的信号并不是像操作系统信号那样以异步回调的方式实现的。它们是在同一个线程中一个接一个地运行,等到某个特定的阶段或事件完成后才会执行。所有注册的信号都会在处理请求的线程中被调用,这样就增加了处理这个请求所需的总时间。

所以从这个角度来看,"信号"这个名字可能会让人误解,但它仍然是有效的,因为信号处理程序会在特定事件被触发时被调用(或者手动发送一些自定义信号)。

2

Django信号并不支持异步功能。

当信号被发送时,所有的接收者就像普通函数一样在同一个线程中被执行。所有的指令都是一步一步执行的。Django在所有接收者完成工作之前,不会对客户端做出响应。

Django信号和套接字信号没有任何关系,后者可以让你订阅某个事件并在不阻塞线程的情况下监听它。

举个例子,这里是Django代码中模型的一部分:

    meta = cls._meta
    if not meta.auto_created:
        signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using,
                              update_fields=update_fields)
    with transaction.commit_on_success_unless_managed(using=using, savepoint=False):
        if not raw:
            self._save_parents(cls, using, update_fields)
        updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)

在代码中,signals.pre_save.send会执行所有接收者。在它们完成之前,Python不会继续执行下一行代码。

对于Django来说,最流行的解决方案是Celery,正如你提到的。另一种方法是使用非阻塞的服务器框架,比如Tornado

撰写回答