我应该如何存储来自Django的长时间运行进程的状态?

5 投票
3 回答
2444 浏览
提问于 2025-04-15 11:32

我正在开发一个Django应用,允许用户上传文件。在把这些文件发送到亚马逊S3之前,我需要在服务器上对这些文件进行一些处理。看过关于这个问题的回答和这篇博客后,我决定最好的处理方式是让我的视图处理器调用Pyro远程对象的方法,异步地进行处理,然后立即返回一个Http 200的响应给客户端。我已经做了原型,似乎效果不错,但我还想存储处理的状态,以便客户端可以查询应用程序,看看文件是否已经处理并上传到S3。

我可以很容易地处理查询,但我不确定在哪里存储处理状态比较合适。这个状态需要能够被Pyro进程写入,同时也能被我的查询视图读取。

  • 我不太想在数据库中添加列来存储那些其实只需要保留30到60秒的数据。
  • 我考虑过使用Django的低级缓存API,用文件ID作为键,但我觉得这并不是缓存框架的设计初衷,也不确定这样做会有什么意想不到的问题。
  • 最后,我考虑过把状态存储在处理的Pyro对象中,但这样看来,我还是需要在数据库中添加一个布尔值“processing_complete”列,以便视图知道是否需要从Pyro对象查询状态。

当然,把状态和数据库分开存储也会有一些数据完整性的问题(如果服务器崩溃了,这些数据都在内存中怎么办?)。我想听听更有经验的网页应用开发者会如何处理这种有状态的处理。

3 个回答

1

所以,你需要的是一个工作队列。在你的情况下,我建议使用数据库来保存状态,即使这些状态存在的时间很短。听起来这样可以满足你的所有需求,而且实现起来也不是特别难,因为你已经有了所有必要的部分。保持简单,除非你真的需要更复杂的东西。

如果你需要更强大或更复杂的东西,我建议你看看 Gearman

5

我知道这个问题已经很老了,但也许有人在这个时候会觉得我的回答有用,所以我就来分享一下。

当然,你可以把数据库当作队列来用,但其实有一些专门为这个目的开发的解决方案。

AMQP就是为了这个而设计的。它可以和CeleryCarrot一起使用,再加上像RabbitMQZeroMQ这样的中间服务器。

我们在最新的项目中就是用的这些,效果很好。

对于你的问题,Celery和RabbitMQ看起来是最合适的选择。RabbitMQ可以确保你的消息不会丢失,而Celery则提供了简单的方式来查看并检查并行运行的进程状态。

你可能还会对octopy感兴趣。

6

我们在数据库里有一个“请求”表来处理这个问题。

当文件上传到来时,我们会创建一个文件对象,并同时创建一个请求记录。

接着,我们启动一个后台处理程序。

然后,我们返回一个200的页面,告诉用户“我们正在处理中”——这个页面会显示请求和它们的状态。

我们的后台处理程序使用Django的ORM工具。当处理完成后,它会更新请求记录。我们可以选择发送邮件通知用户,但通常我们只是更新状态,让用户可以再次登录查看处理是否完成。


关于批处理服务器架构的说明。

这是一个WSGI服务器,它在某个端口上等待批处理请求。这个请求是一个带有ID号的REST POST请求;批处理程序会在数据库中查找这个ID并进行处理。

服务器是通过我们的REST接口自动启动的。如果它没有运行,我们会启动它。这可能会让用户的操作感觉比较慢,不过没办法。它不应该崩溃。

此外,我们还有一个简单的定时任务(crontab)来检查它是否在运行。最多会在“你还活着吗?”的检查之间停机30分钟。我们没有正式的启动脚本(我们是在Apache下用mod_wsgi运行的),但我们可能会创建一个“重启”脚本,更新WSGI文件,然后向一个健康检查的URL发送POST请求(并启动批处理程序)。

当批处理服务器启动时,可能会有一些未处理的请求,因为它之前没有收到POST请求。所以,默认的启动方式是从请求队列中提取所有的工作——假设它可能错过了什么。

撰写回答