Асинхронная обработка Django

У меня есть куча запросов Django, которые выполняют некоторые математические вычисления (написанные на C и выполняемые через модуль Cython), которые могут выполнять неопределенное количество времени (порядка 1 секунды). Также запросам не требуется доступ к базе данных, и все они независимы друг от друга и от Django.

Прямо сейчас все синхронно (используя Gunicorn с sync рабочими типами), но я хотел бы сделать это асинхронным и неблокирующим. Короче говоря, я хотел бы сделать что-то:

  1. Получите AJAX-запрос
  2. Выделить задачу доступному работнику (без блокировки основного веб-приложения Django)
  3. Рабочий выполняет задачу в неизвестное количество времени
  4. Django возвращает результат вычисления (список строк) в виде JSON всякий раз, когда задача завершается.

Я очень новичок в асинхронном Django, поэтому мой вопрос в том, какой стек лучше всего подходит для этого.

Подходит ли для такого рода процессов очередь задач? Кто-нибудь порекомендует Tornado + Celery + RabbitMQ или, возможно, что-то еще?

Заранее спасибо!


person Stephen Diehl    schedule 26.12.2010    source источник
comment
Что делать с результатами вычислений?   -  person Sam Dolan    schedule 27.12.2010
comment
Верните результат (в формате JSON) в браузер пользователя.   -  person Stephen Diehl    schedule 27.12.2010


Ответы (2)


Для этого идеально подойдет сельдерей.

Поскольку то, что вы делаете, относительно просто (читай: вам не нужны сложные правила о том, как должны быть маршрутизированы задачи), вы, вероятно, могли бы обойтись без использования бэкэнда Redis, что означает, что вам не нужно устанавливать/настраивать RabbitMQ (что, по моему опыту, сложнее).

Я использую Redis с наиболее развитой сборкой Celery, и вот соответствующие части моей конфигурации:

# Use redis as a queue
BROKER_BACKEND = "kombu.transport.pyredis.Transport"
BROKER_HOST = "localhost"
BROKER_PORT = 6379
BROKER_VHOST = "0"

# Store results in redis
CELERY_RESULT_BACKEND = "redis"
REDIS_HOST = "localhost"
REDIS_PORT = 6379
REDIS_DB = "0"

Я также использую django-celery, что делает интеграцию с Django счастливой.

Напишите, если вам нужен более конкретный совет.

person David Wolever    schedule 27.12.2010
comment
Кроме того, у меня не было никаких проблем с использованием gevent + исправление обезьяны с Celery, поэтому, если вы используете gevent воркера пушки и celery обезьяны, все должно быть просто волшебным образом асинхронно. - person David Wolever; 27.12.2010
comment
Спасибо за совет. Раньше я пытался использовать gevent worker + исправление обезьяны, но это замедляло работу моего приложения. Я подозреваю, что это связано с тем, что я блокирую соединение с MySQL. Нужно ли мне переходить на другую базу данных? - person Stephen Diehl; 27.12.2010
comment
К сожалению, я не работал с gevent и другими БД, поэтому не могу сказать. Может быть, опубликовать еще один вопрос, спрашивая об этом? - person David Wolever; 27.12.2010
comment
Драйвер MySQLdb написан на C и не использует модуль сокетов Python. Это означает, что он не может быть исправлен обезьяной и заблокирует весь цикл gevent. github.com/mthurlin/gevent-MySQL — это асинхронная версия. - person Alex Recarey; 27.02.2011
comment
Я большой поклонник Celery и довольно часто использую его для более длительных задач, в которых мне не нужно ждать результата. Для варианта использования OP кажется, что ожидание того, что работник сельдерея завершит задачу асинхронно, чтобы мы могли затем ответить на запрос AJAX, по-прежнему будет блокировать веб-приложение Django, верно? - person mateolargo; 04.10.2011
comment
Да, если бы вызов был чем-то вроде my_task.apply(…), поток Django заблокировался бы в ожидании завершения задачи. Это может иметь смысл, если, например, задача сильно привязана к процессору… Но это не освобождает поток Django. - person David Wolever; 04.10.2011

Поскольку вы планируете сделать его асинхронным (предположительно, используя что-то вроде gevent), вы также можете рассмотреть возможность создания многопоточного/разветвленного серверного веб-сервиса для вычислительной работы.

Асинхронный интерфейсный сервер может выполнять всю легкую работу, получать данные из баз данных, которые подходят для асинхронного режима (redis или mysql со специальным драйвером) и т. д. Когда необходимо выполнить вычисление, внешний интерфейсный сервер может отправить все входные данные в бэкэнд-сервер и получить результат, когда бэкэнд-сервер завершит его вычисление.

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

client browser <> async frontend server <> backend server for computations
person konrad    schedule 14.09.2011