Django:我应该开始一个单独的过程吗?

时间:2010-11-27 13:36:44

标签: django process

我正在编写一个允许用户在文件中上传数据的应用程序;该应用程序将处理此数据,并将结果通过电子邮件发送给用户。处理可能需要一些时间,所以我想在Python脚本中单独处理它,而不是在视图中等待它完成。 Python脚本和视图不需要通信,因为脚本将从视图写入的文件中获取数据。视图将显示一条消息,例如“感谢您上传数据 - 结果将通过电子邮件发送给您”

在Django中执行此操作的最佳方法是什么?产生一个单独的过程?把东西放在队列上?

非常感谢一些示例代码。感谢。

4 个答案:

答案 0 :(得分:17)

最简单的解决方案是编写自定义commands,搜索所有未处理的文件,处理它们,然后通过电子邮件发送给用户。管理命令在Django框架内运行,因此可以访问所有模型,数据库连接等,但您可以从任何地方调用它们,例如crontab。

如果您关心文件上传和处理开始之间的时间范围,您可以使用像Celery这样的框架,它基本上是一个帮助库,用于使用消息队列和运行工作人员监听队列。这将是相当低的延迟,但另一方面,简单性对您来说可能更重要。

我强烈建议不要在视图中启动线程或生成进程,因为线程将在django进程内运行并可能破坏您的Web服务器(取决于您的配置)。子进程将继承您可能不想要的Django进程中的所有内容。最好把这些东西分开。

答案 1 :(得分:4)

我目前有一个具有类似要求的项目(只是更复杂的^^)。

永远不要从Django视图中生成子进程或线程。你无法控制Django进程,它可能会在任务结束前被杀死,暂停等。它由Web服务器控制(例如,通过WSGI的apache)。

我要做的是一个外部脚本,它将在一个单独的进程中运行。我认为你有两个解决方案:

  • 始终运行并抓取放置文件的目录的进程。例如,它将每隔十秒检查一次目录并处理文件
  • 与上面相同,但每隔x秒由cron运行一次。这基本上具有相同的效果
  • 使用Celery创建工作进程并使用Django应用程序将作业添加到队列中。然后,您需要通过Celery提供的其中一种方法获得结果。

现在您可能需要访问Django模型中的信息,最后通过电子邮件发送给用户。这里有几个解决方案:

  • 从外部脚本
  • 导入模块(模型等)
  • 将外部脚本实现为自定义命令(如knutin建议的那样)
  • 例如,通过POST请求将结果传递给Django应用程序。然后你会在正常的Django视图中进行电子邮件发送和状态更改等。

我会去外部进程并导入模块或POST请求。这样它更灵活。例如,您可以使用多处理模块同时处理多个文件(从而有效地使用多核机器)。

基本工作流程将是:

  1. 检查目录中的新文件
  2. 对于每个文件(可以并行化):
    1. 过程
    2. 发送电子邮件或通知您的Django应用程序
  3. 睡了一会儿
  4. 我的项目包含真正需要CPU的处理。我目前使用一个外部进程,为一个工作进程池提供处理作业(这基本上就是Celery可以为你做的),并通过POST请求将进度和结果报告给Django应用程序。它工作得很好并且相对可扩展,但我很快就会改变它以在集群上使用Celery。

答案 2 :(得分:3)

您可以生成thread来进行处理。这与Django没什么关系;视图函数需要启动工作线程,就是这样。

如果您真的想要一个单独的流程,则需要subprocess模块。但是,您真的需要重定向标准I / O还是允许外部过程控制?

示例:

from threading import Thread
from MySlowThing import SlowProcessingFunction # or whatever you call it

# ...

Thread(target=SlowProcessingFunction, args=(), kwargs={}).start()

我还没有完成一个我不想跟踪线程进度的程序,所以我不知道这是否有效而没有在某处存储Thread对象。如果你需要这样做,那很简单:

allThreads = []

# ...

global allThreads
thread = Thread(target=SlowProcessingFunction, args=(), kwargs={})
thread.start()
allThreads.append(thread)

thread.is_alive()返回False时,您可以从列表中删除帖子:

def cull_threads():
    global allThreads
    allThreads = [thread for thread in allThreads if thread.is_alive()]

答案 3 :(得分:1)

您可以使用多处理。 http://docs.python.org/library/multiprocessing.html

基本上,

def _pony_express(objs, action, user, foo=None):
    # unleash the beasts

def bulk_action(request, t):

    ...
    objs = model.objects.filter(pk__in=pks)

    if request.method == 'POST':
        objs.update(is_processing=True)

        from multiprocessing import Process
        p = Process(target=_pony_express, args=(objs, action, request.user), kwargs={'foo': foo})
        p.start()

        return HttpResponseRedirect(next_url)

    context = {'t': t, 'action': action, 'objs': objs, 'model': model}
    return render_to_response(...)