асинхронное выполнение задач для веб-приложения - PullRequest
3 голосов
/ 17 ноября 2009

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

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

Теперь мой вопрос не относится к дизайну (хотя мне бы очень понравились любые подсказки по этому поводу). Мой вопрос: существует ли система для решения этой проблемы, поэтому я не изобретаю квадратное колесо? Если бы мне пришлось это сделать, я бы использовал диспетчер очереди процессов для отправки задачи и поставил бы конечную точку HTTP для определения состояния, что-то вроде «в ожидании», «прервано», «выполнено» для клиента ajax, но если что-то похожее уже существует специально для этой задачи, мне бы в основном это понравилось.

Я работаю в Python + Django.

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

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

Редактировать 2 : Я добавил награду, чтобы посмотреть, смогу ли я получить какой-то другой ответ. Я проверил pyprocessing, но не могу выполнить отправку задания и повторно подключиться к очереди на более позднем этапе.

Ответы [ 7 ]

4 голосов
/ 02 декабря 2009

Вы не должны изобретать колесо здесь.

Выезд Шестерня . Он имеет библиотеки на многих языках (включая python) и довольно популярен. Не уверен, есть ли у кого-нибудь из коробки способы легко соединить django с вызовами gearman и ajax, но делать эту часть не составит труда.

Основная идея заключается в том, что вы запускаете сервер заданий gearman (или несколько серверов заданий), чтобы ваш веб-запрос поставил в очередь задание (например, «resize_photo») с некоторыми аргументами (например, «{photo_id: 1234}»). Вы ставите это в очередь в качестве фоновой задачи. Вы получаете ручку обратно. Затем ваш ajax-запрос будет опрашивать значение этого дескриптора, пока оно не будет помечено как завершенное.

Затем у вас есть рабочий (или, возможно, многие), который является отдельным процессом python, подключенным к этому серверу заданий и регистрирующим себя для заданий 'resize_photo', выполняет работу и затем помечает ее как завершенную.

Я также нашел это сообщение в блоге , которое довольно неплохо суммирует его использование.

1 голос
/ 01 декабря 2009

Сначала вам понадобится отдельная «рабочая» служба, которая будет запускаться отдельно при включении питания и связываться с обработчиками http-запросов через некоторые локальные IPC, такие как UNIX-сокет (быстрый) или база данных (простой).

Во время обработки запроса cgi запрашивает состояние рабочего или другие данные и воспроизводит их клиенту.

1 голос
/ 30 ноября 2009

Я не знаю системы, которая это делает, но было бы довольно легко реализовать собственную систему:

  • создать таблицу базы данных с идентификатором задания, параметрами задания, результатом задания
    • jobresult - строка, которая будет содержать выборку результата
    • jobparameters - маринованный список входных аргументов
  • когда сервер начинает работать над заданием, он создает новую строку в таблице и запускает новый процесс для обработки этого, передавая этот процесс jobid
  • процесс обработчика задач обновляет результат задания в таблице после его завершения
  • веб-страница (xmlrpc или что вы используете) содержит метод 'getResult (jobid)', который проверит таблицу на результат работы
    • если он находит результат, он возвращает результат и удаляет строку из таблицы
    • в противном случае он возвращает пустой список, или None, или предпочитаемое возвращаемое значение, чтобы указать, что задание еще не завершено

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

1 голос
/ 17 ноября 2009

Вы можете попробовать два подхода:

  • Кому звонить веб-серверу каждый n интервал и сообщать идентификатор задания; сервер обрабатывает и возвращает некоторую информацию о текущем выполнении этой задачи
  • Чтобы реализовать долго работающую страницу, отправка данных каждый n интервал; для клиента этот HTTP-запрос «всегда» будет "loading", и он должен собирать новую информацию каждый раз, когда поступает новый фрагмент данных.

О втором варианте вы можете узнать больше, прочитав о Comet ; Используя ASP.NET, вы можете сделать что-то похожее, реализовав System.Web.IHttpAsyncHandler интерфейс.

0 голосов
/ 18 февраля 2012

Еще один хороший вариант для python и django - это Сельдерей .

И если вы считаете, что Celery слишком тяжел для ваших нужд, вы можете взглянуть на простую распределенную taskqueue .

0 голосов
/ 02 декабря 2009

Возможно, это не лучший ответ для решения python / django, с которым вы работаете, но мы используем Microsoft Message Queue для таких вещей. Это в основном работает так:

  1. Веб-сайт обновляет строку базы данных где-либо со статусом «Обработка»
  2. Сайт отправляет сообщение в MSMQ (это не блокирующий вызов, поэтому он сразу же возвращает управление на сайт)
  3. Служба Windows (на самом деле это может быть любая программа) "наблюдает" за MSMQ и получает сообщение
  4. Служба Windows обновляет строку базы данных со статусом «Завершено».

В этом и есть суть. Это было довольно надежно для нас и действительно легко масштабировать и управлять.

-al

0 голосов
/ 17 ноября 2009

Вы можете сообщить, что ресурс «обрабатывается», ответив HTTP-кодом 202: клиентской стороне придется повторить попытку позже, чтобы получить заполненный ресурс. В зависимости от случая вам может потребоваться ввести «идентификатор запроса», чтобы сопоставить запрос с ответом.

Кроме того, вы можете взглянуть на существующие библиотеки COMET, которые могут удовлетворить ваши потребности "из коробки". Я не уверен, что есть какие-либо, которые соответствуют вашему текущему дизайну Django.

...