Django: должен ли я начать отдельный процесс?

Я пишу приложение, которое позволит пользователю загружать данные в файл; приложение будет обрабатывать эти данные, и по электронной почте результаты для пользователя. Обработка может занять некоторое время, поэтому я хотел бы обработать это отдельно в скрипте Python, а не ждать в представлении его завершения. Скрипт Python и представление не должны взаимодействовать, поскольку скрипт будет собирать данные из файла, написанного представлением. В представлении просто появится сообщение типа " Спасибо за загрузку ваших данных-результаты будет отправлено вам по электронной почте"

какой лучший способ сделать это в Django? Создать отдельный процесс? Поставить что-нибудь в очередь?

некоторый пример кода был бы весьма признателен. Спасибо.

4 ответов


самое простое возможное решение-написать пользовательский команды, который ищет все необработанные файлы, обрабатывает их, а затем отправляет по электронной почте пользователю. Команды управления выполняются внутри Django framework, поэтому они имеют доступ ко всем моделям, подключениям к БД и т. д., Но вы можете вызывать их откуда угодно, например crontab.

Если вы заботитесь о таймфрейме между загруженным файлом и началом обработки, вы можете использовать фреймворк, такой как сельдерей, который в основном является вспомогательной библиотекой для использования очереди сообщений и запуска рабочих, прослушивающих очередь. Это будет довольно низкая задержка, но, с другой стороны, простота может быть более важна для вас.

Я бы настоятельно рекомендовал не запускать потоки или нерестовые процессы в ваших представлениях, поскольку потоки будут работать внутри процесса django и могут уничтожить ваш веб-сервер(в зависимости от вашей конфигурации). Дочерний процесс унаследует все из процесса Django, который вы, вероятно, не хотите. Лучше держать все это отдельно.


в настоящее время у меня есть проект с аналогичными требованиями (просто более сложный^^).

никогда не порождайте подпроцесс или поток из вашего представления Django. У вас нет контроля над процессами Django, и его можно убить, приостановить и т. д. до конца задачи. Он управляется веб-сервером (например, apache через WSGI).

то, что я бы сделал, это внешний скрипт, который будет работать в отдельном процессе. У вас есть два решения, я думаю :

  • процесс это всегда работает и обходит каталог, в который вы помещаете свои файлы. Например, он будет проверять каталог каждые десять секунд и обрабатывать файлы
  • то же, что и выше, но выполняется cron каждые X секунд. Это в основном имеет тот же эффект
  • используйте сельдерей для создания рабочих процессов и добавления заданий в очередь с помощью приложения Django. Затем вам нужно будет вернуть результаты одним из средств, доступных с сельдереем.

теперь вы, наверное необходимо получить доступ к информации в моделях Django, чтобы отправить пользователю по электронной почте в конце. Здесь у вас есть несколько решений :

  • импортировать модули (модели и т. д.) Из внешнего скрипта
  • реализовать внешний скрипт как пользовательскую команду (как предложил кнутин)
  • сообщите результаты в приложение Django с помощью запроса POST, например. Затем вы сделаете отправку электронной почты и изменения статуса и т. д. В обычном представлении Django.

I пойдет на внешний процесс и импортирует модули или запрос POST. Таким образом, он гораздо более гибок. Например, вы можете использовать модуль многопроцессорной обработки для обработки нескольких файлов одновременно (таким образом, эффективно используя многоядерные машины).

основной рабочий процесс будет такой:

  1. проверяем каталог на наличие новых файлов
  2. для каждого файла (можно распараллелить):
    1. процесс
    2. отправить по электронной почте или уведомить Django применение
  3. поспи немного

мой проект содержит действительно требовательную к процессору обработку. В настоящее время я использую внешний процесс, который дает задания обработки пулу рабочих процессов (это в основном то, что сельдерей может сделать для вас) и сообщает о прогрессе и результатах в приложение Django через POST-запросы. Он работает очень хорошо и относительно масштабируется, но я скоро изменю его, чтобы использовать сельдерей в кластере.


вы могли бы породить нить для обработки. Это не имело бы большого отношения к Django; функция view должна была бы начать рабочий поток, и все.

если вы действительно хотите отдельный процесс, вам понадобится подпроцесс. Но вам действительно нужно перенаправить стандартный ввод-вывод или разрешить внешний контроль процесса?

пример:

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()]

вы можете использовать многопроцессорность. 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(...)