【问题标题】:Django: Should I kick off a separate process?Django:我应该启动一个单独的进程吗?
【发布时间】:2011-01-12 13:34:15
【问题描述】:

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

在 Django 中最好的方法是什么?产生一个单独的进程?把东西放在队列上?

我们将不胜感激一些示例代码。谢谢。

【问题讨论】:

  • 如果在处理数据时出现错误怎么办?
  • 我会给他们发电子邮件。我不能指望他们在网页上等到完成,因为这可能需要 20 分钟或更长时间。

标签: django process


【解决方案1】:

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

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

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

【讨论】:

  • 是的,我希望它尽可能简单,但没有意识到产生进程或线程的结果可能如此严重。感谢您的提醒。
【解决方案2】:

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

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

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

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

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

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

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

一个基本的工作流程是:

  1. 检查目录是否有新文件
  2. 对于每个文件(可以并行化):
    1. 处理
    2. 发送电子邮件或通知您的 Django 应用程序
  3. 睡一会儿

我的项目包含真正需要 CPU 的处理。我目前使用一个外部进程,它将处理作业提供给一个工作进程池(这基本上是 Celery 可以为你做的),并通过 POST 请求将进度和结果报告给 Django 应用程序。它工作得非常好并且相对可扩展,但我很快会将它更改为在集群上使用 Celery。

【讨论】:

  • 感谢您的宝贵反馈。我可能需要为大文件启动多个线程,因此将查看多处理模块。
  • 如果处理受 CPU 限制,则必须使用进程(例如,使用处理模块)而不是线程(线程模块)。 Python Global Interpreter Lock 阻止线程真正并行运行,因此并没有提高性能(我在做项目时意识到了这一点)。
  • 另外,Celery 可能是一个不错的选择,因为它自动完成了我描述的很多工作。唯一的事情是你必须弄清楚如何获得结果,因为你不能等待任务完成(我认为 HTTP 回调可以轻松完成)。
【解决方案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()]

【讨论】:

  • 我只需要启动一个 python 脚本就可以了。我不需要与它通信,因为它会从我在视图中写入的文件中获取数据。你能添加一些示例代码吗?
  • 是完全独立的Python脚本,还是可以作为模块导入并调用?
  • @Mike DeSimone。这是一个完全独立的 Python 脚本,但在得到 knutin 的反馈后,最好不要生成 proc 或线程。
  • @Mike DeSimone。感谢您的示例代码。我现在也在查看自定义管理命令选项。
  • 线程会杀死运行服务器,不是吗?这个策略最好用多进程来实现,不是吗?
【解决方案4】:

您可以使用多处理。 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(...)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-10
    • 2018-05-12
    • 2021-12-19
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多