Подтвердить что ты не робот

Django длительные асинхронные задачи с потоками/обработкой

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

Возникает вопрос:

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

Скажем:

  • мой "long_process" делает что-то действительно безопасное. даже если это терпит неудачу, мне все равно.
  • python >= 2.6
  • Я использую 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)

(где script скорее всего будет командой manage.py)

темы

import threading

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

NB.

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

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

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

from multiprocessing import Process

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

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


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

4b9b3361

Ответ 1

os.fork

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

подпроцесс

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

<сильные > темы

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

многопроцессорной

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

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

Ответ 2

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

Для начала использования 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 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

За вопрос:

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

ответ - нет.

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

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

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