为什么Django的"request_finished"信号会阻塞响应?
如果我从一个网站发起一个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。