在Django Web 应用中运行异步 Python 代码

5

在Django web应用程序中,异步运行某些代码是否可行?如果可以,如何实现?

例如:

我有一个返回数百或数千个结果的搜索算法。我想要将这些项目输入到数据库中,以便查看用户搜索最多的内容。我不希望客户端需要等待额外的一百或一千个数据库插入操作。是否有一种方法可以异步执行此操作?这样做是否存在任何风险?还有更好的方法来实现吗?

2个回答

3
就 Django 而言,是的。
更大的问题在于您的 Web 服务器以及它是否与线程协作。例如,gunicorn 的同步工作者是单线程的,但有其他引擎,如 greenlet。我不确定它们与线程的兼容性如何。
如果从线程进行分叉,则将线程和多进程组合可能会成为一个问题: Python 中混合多进程和线程的状态

http://bugs.python.org/issue6721

话虽如此,我知道一些流行的性能分析工具使用线程来报告指标,因此这似乎是一种被接受的做法。

总之,最安全的方法似乎是使用标准库中的 threading.Thread 对象,只要你在其中执行的任何操作不会分叉(Python 的 multiprocessing 库)。

https://docs.python.org/2/library/threading.html


3
从主线程卸载请求是一种常见的做法,因为最终目标是尽快向客户端(浏览器)返回结果。
正如您所知,HTTP 是阻塞的 - 因此在您返回响应之前,客户端无法执行任何操作(它被阻塞在等待状态)。
卸载请求的事实标准方式是通过 celery 这个任务排队系统。
我强烈建议您阅读 introduction to celery 主题,但简而言之,以下是发生的情况:
  1. 您将某些代码标记为“任务”。这通常是您想异步运行的函数。

  2. Celery 管理工作进程 - 您可以将其视为线程 - 以运行这些任务。

  3. 要与工作进程通信,需要使用 消息队列RabbitMQ 是经常推荐的消息队列。

一旦您的所有组件都运行起来了(只需要几分钟);您的工作流程如下:
  1. 在您的视图中,当您想要卸载一些工作时,您将使用 .delay() 选项调用执行该工作的函数。这将触发工作者在后台执行方法。

  2. 然后,您的视图立即返回响应。

  3. 然后,您可以检查任务的结果,并根据需要采取适当的行动。也有跟踪进度的方式

还可以很好地使用缓存 - 这样就不会不必要地执行昂贵的任务。例如,您可能选择卸载请求以对搜索关键字进行分析,并将其放置在报告中。

生成报告后,如果适用,我会缓存结果,以便在以后请求时可以显示相同的报告 - 而不是再次生成。


2
Celery是绝对有效的,但需要额外的系统架构。如果一个任务足够重,需要大量的RAM或CPU来保持Web服务器不受负担,我会毫不犹豫地使用它。在我看来,只有当基础架构已经存在或请求足够慢时,使用Celery转发Web请求才是有用的,因为将任务发布到AMQP服务器本身就是一种请求,这使得它有些多余。调用Celery任务的代码比多线程更简单,但不要以增加更多的基础设施费用/维护成本为代价。 - Meridius
您不需要“基础设施”,可以在与现有应用程序相同的计算机上运行celery,而不会影响应用程序的性能(因为Web服务器不受CPU限制)。 - Burhan Khalid
2
我认为任何需要与主服务器并行运行的内容都被视为基础设施。它是一个外部实体,需要维护(系统升级、监控、适当的防火墙设置等)。此外,说Web服务器不受CPU限制有点笼统,因为这取决于服务器如何/以及运行什么。另外,如果CPU被外部任务完全占用或系统内存不足,服务器的响应性将会失败,或者它可能会被直接杀死(oomkiller)。再次强调,我认为Celery是一个有效的方法(易于调用,允许增长等)。 - Meridius
1
然而,由于Celery是流行的方法,我想提供一个反驳意见,说明为什么更轻量级的解决方案有时可能更合适。最终,这取决于项目的复杂性、将要并行运行的不同任务数量以及这些任务的重量,这些因素会倾向于某一方面。 - Meridius
@BurhanKhalid 当任务开始并且页面被渲染后,您如何查询它们? - SumNeuron

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