Django долго запускает асинхронные задачи с потоками / обработкой - PullRequest
29 голосов
/ 09 ноября 2011

Отказ от ответственности : я знаю, что есть несколько похожих вопросов по SO. Я думаю, что прочитал большинство, если не все из них, но не нашел ответа на свой настоящий вопрос (см. Позже). Я также знаю, что использование сельдерея или других асинхронных систем очередей - это лучший способ для выполнения долгосрочных задач или, по крайней мере, использование сценария, управляемого cron. Есть также документ mod_wsgi о процессах и потоках , но я не уверен, что все понял правильно.

Вопрос:

Каковы точные риски / проблемы, связанные с использованием решений, перечисленных ниже? Является ли какой-либо из них жизнеспособным для длительных задач (хорошо, хотя сельдерей лучше подходит)? Мой вопрос на самом деле больше о понимании внутренностей wsgi и python / django, чем о поиске лучшего общего решения. Проблемы с блокировкой потоков, небезопасным доступом к переменным, обработкой зомби и т. Д.

Скажем так:

  1. мой "long_process" делает что-то действительно безопасное. даже если это не помогает, мне все равно.
  2. питон> = 2,6
  3. Я использую mod_wsgi с apache (что-нибудь изменится с uwsgi или gunicorn?) В режиме демона

mod_wsgi conf:

WSGIDaemonProcess NAME user=www-data group=www-data threads=25
WSGIScriptAlias / /path/to/wsgi.py
WSGIProcessGroup %{ENV:VHOST}

Я полагал, что это опции, доступные для запуска отдельных процессов (в широком смысле) для выполнения длительной задачи при быстром возвращении ответа пользователю:

os.fork

import os

if os.fork()==0:
    long_process()
else:
    return HttpResponse()

подпроцесс

import subprocess

p = subprocess.Popen([sys.executable, '/path/to/script.py'], 
                                    stdout=subprocess.PIPE, 
                                    stderr=subprocess.STDOUT)

(где сценарий может быть командой manage.py)

тема

import threading

t = threading.Thread(target=long_process,
                             args=args,
                             kwargs=kwargs)
t.setDaemon(True)
t.start()
return HttpResponse()

Примечание.

Из-за Глобальной блокировки интерпретатора в CPython только один поток может выполнять код Python одновременно (даже если некоторые ориентированные на производительность библиотеки могут преодолеть это ограничение). Если вы хотите, чтобы ваше приложение лучше использовало вычислительные ресурсы многоядерных машин, рекомендуется использовать многопроцессорность. Тем не менее, многопоточность по-прежнему является подходящей моделью, если вы хотите запускать несколько задач, связанных с вводом / выводом одновременно.

Основной поток быстро вернется (httpresponse). Будет ли порожденная длинная нить блокировать wsgi делать что-то еще для другого запроса ?!

многопроцессорная

from multiprocessing import Process

p = Process(target=_bulk_action,args=(action,objs))
p.start()
return HttpResponse()

Это должно решить проблему параллелизма потоков, не так ли?


Так что вот варианты, которые я мог придумать. Что будет работать, а что нет и почему?

Ответы [ 3 ]

26 голосов
/ 10 ноября 2011

os.fork

Форк клонирует родительский процесс, который в данном случае является вашим стеком Django.Поскольку вы просто хотите запустить отдельный скрипт на Python, это выглядит как ненужное количество раздувания.

подпроцесс

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

потоков

Потокиопределенные единицы логики.Они начинаются, когда вызывается их метод run(), и заканчиваются, когда заканчивается выполнение метода run().Это делает их хорошо подходящими для создания ветви логики, которая будет работать за пределами текущей области.Однако, как вы упомянули, на них распространяется Глобальная блокировка интерпретатора.

многопроцессорная обработка

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

Итак, ваш выбор действительно сводится к потокам или многопроцессорности.Если вы можете обойтись с потоком, и это имеет смысл для вашего приложения, переходите с потока.В противном случае используйте многопроцессорность.

11 голосов
/ 01 февраля 2013

Я обнаружил, что с помощью uWSGI Decorators довольно проще, чем с помощью Celery, если вам нужно просто запустить длинную задачу в фоновом режиме.Думайте, что Celery - лучшее решение для серьезного тяжелого проекта, и это требует дополнительных усилий.

Для начала использования uWSGI Decorators вам просто нужно обновить конфигурацию uWSGI с помощью

<spooler-processes>1</spooler-processes>
<spooler>/here/the/path/to/dir</spooler>

написать код, например:

@spoolraw
def long_task(arguments):
    try:
        doing something with arguments['myarg'])
    except Exception as e:
        ...something...
    return uwsgi.SPOOL_OK

def myView(request)
    long_task.spool({'myarg': str(someVar)})
    return render_to_response('done.html')

Чем при запуске просмотра в журнале uWSGI отображается:

[spooler] written 208 bytes to file /here/the/path/to/dir/uwsgi_spoolfile_on_hostname_31139_2_0_1359694428_441414

и по завершении задачи:

[spooler /here/the/path/to/dir pid: 31138] done with task uwsgi_spoolfile_on_hostname_31139_2_0_1359694428_441414 after 78 seconds

Тамстранные (для меня) ограничения:

    - spool can receive as argument only dictionary of strings, look like because it's serialize in file as strings.
    - spool should be created on start up so "spooled" code it should be contained in separate file which should be defined in uWSGI config as <import>pyFileWithSpooledCode</import>
3 голосов
/ 10 ноября 2011

На вопрос:

Будет ли порожденная длинная нить блокировать wsgi делать что-то еще для еще один запрос?!

ответ - нет.

Вы все равно должны быть осторожны при создании фоновых потоков из запроса, хотя в случае, если вы просто создаете их огромное количество и забиваете весь процесс. Вам действительно нужна система организации задач, даже если вы делаете что-то в процессе.

В отношении выполнения форка или exec из веб-процесса, особенно из Apache, это, как правило, не очень хорошая идея, поскольку Apache может навязывать странные условия в среде создаваемого подпроцесса, которые могут технически мешать его работе.

Использование системы, подобной Celery, по-прежнему, вероятно, лучшее решение.

...