Pull to refresh

Comments 64

Классная штука, каждый раз собираюсь попробовать.
Вот и мануал удобный есть, я надеюсь.
А чем стандартный крон и management commands не устраивает?
Cron может случить заменой периодических заданий celery, но используя celery вы получаете удобство задания расписания непосредственно внутри приложений django.
Про management commands не понял вопроса — ведь это команды типа syncdb, выполняемые из консоли. Асинхронные задания в celery — это когда вы, например, после выполнения запроса от пользователя, запускаете выполняться что-то ещё, что потребутеся в дальнейшем, а пользователь тем временем уже видит отрендеренную страничку.
можно писать свои management commands и запускать через cron\shell
Главная особенность celery вовсе не в том, что с ее помощью можно запускать периодические задания и использовать вместо cron-а (эта плюшка вообще приделана сбоку, и ее можно не использовать). Больше пользы в том, что долго выполняющееся действие можно легко отвязать от кода, который выполняется во view в ответ на запрос пользователя.

Фактически, используя celery, вы добавляете задание в очередь и на время забываете про него. Затем запущенный отдельно демон по имени celeryd это задание выполняет, а результат выполнения складывает обратно.
Это лишняя сущность. Все то что Вы описали делается, как верно заметил artem_dev, связкой cron+management commands. Причем без установки кучи шлака.

Хотя конечно «перфекионистам с дедлайнами» подойдет, особенно тем кто «ниасилил» синтаксис crontab. Но я бы использовать не стал.
С другой стороны, то же самое можно сказать и про management commands. Зачем писать дополнительную management команду и запускать всё это через шелл если можно обойтись одной строчкой с декоратором?
Если не использовать сторонний «шлак», то этот шлак придется написать самому. Насколько я понял, асинхронные команды вы предлагаете использовать следующим образом (предполагаю, потому что сам так делал, и в некоторых случаях этот подход действительно хорошо работает):

— куда-нибудь в базу пишем команду, которую предполагается выполнить, или каким-то способом отмечаем «необработанные» объекты
— в cron добавляем задание, которое периодически (раз в минуту) проверяет список и выполняет действия с необработанными объектами.

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

— если задание не успевает выполниться за 1-2 минуты, то последующие задания начинают «наступать на пятки» уже выполняющемуся, и нужно придумывать блокировки. Либо на уровне задания cron (flock), либо на уровне записей в базе (вводить состояния задач: «не выполнялось», «выполняется», «выполнено»). В celery эта задача уже решена.
— если заданий у вас становится больше, чем 1-2, то либо для каждого задания (resize картинки, получение rss фида с внешнего сервера, загрузка товаров в базу и т.п.) вам нужно писать отдельную management команду, либо придумывать способ выделить общую часть, отвечающую за выполнение команд. В celery код, который непосредственно занимается работой, отделяется от кода, который занимается управлением очереди, с помощью декоратора task. Довольно элегантно, на коленке такое не скрутишь.
— если вам нужно отслеживать результат выполнения команды (было бы клево сообщить пользователю, что всё сделано), то нужно придумать отдельное хранилище для этого результата, и удобный способ его получения.

Ну, и так далее. Не факт, что при возникновении подобных проблем решить их у вас получится быстрее и лучше, чем у перфекционистов с дедлайнами, не освоившими синтаксис crontab.
Ну, я потому и сослался на перфекционизм — это будет работать замечательно если раньше не писал связку cron + commands. Но, написав однажды, получается не менее гибкий механизм. Статусы и локи, которые Вы упомянули — достаточно тривиальная вещь.

Встречный вопрос, раз уж мы заговорили про трудоемкие задачи (вроде генерации большой пачки pdf или ресайза картинок) — как на счет nice? Позволяет описанная система управлять приоритетами?
Опять же, если вдруг что-то пошло не так, команду можно банально выполнить вручную и глянуть что происходит. А как с этим в Celery?
Никто не говорит, что добавить к задаче статус выполнения — это сложно. Речь идет о том, что это уже получается не просто «связка cron+management commands», а «связка cron+management commands+дополнительная логика, про которую нужно не забыть, и которую нужно сделать».

С задачей расстановки приоритетов я не сталкивался, но принципиально не вижу никаких сложностей. Тот, что лежит на поверхности — запустить несколько демонов celeryd, каждый из которых будет обрабатывать свою очередь. Задачи разных приоритетов будут раскладываться в разные очереди. Интенсивность загрузки можно регулировать количеством процессов демона, обслуживающего очередь, или запустив один из демонов под найсом.

> Опять же, если вдруг что-то пошло не так, команду можно банально выполнить вручную и глянуть что происходит. А как с этим в Celery?

Создавая задачу, вы навешивает декоратор на функцию. Эта же функция может быть вызвана и напрямую, без обращения к брокеру.
Никто не говорит, что добавить к задаче статус выполнения — это сложно. Речь идет о том, что это уже получается не просто «связка cron+management commands», а «связка cron+management commands+дополнительная логика, про которую нужно не забыть, и которую нужно сделать».

С задачей расстановки приоритетов я не сталкивался, но принципиально не вижу никаких сложностей. Например, довольно просто запустить несколько демонов celeryd, каждый из которых будет обрабатывать свою очередь. Задачи разных приоритетов будут раскладываться в разные очереди. Интенсивность загрузки можно регулировать количеством процессов демона, обслуживающего очередь, или запустив один из демонов под найсом.

> Опять же, если вдруг что-то пошло не так, команду можно банально выполнить вручную и глянуть что происходит. А как с этим в Celery?

Создавая задачу, вы навешивает декоратор на функцию. Эта же функция может быть вызвана и напрямую, без обращения к брокеру.
Сорри, не удержался, и ответил дважды.
Ну конечно же cron+custom management commands не замена фиче асинхронных вычислений celery, изначальный мой коммент относился к фиче celery запускать периодические задания.

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

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

"— если вам нужно отслеживать результат выполнения команды (было бы клево сообщить пользователю, что всё сделано), то нужно придумать отдельное хранилище для этого результата, и удобный способ его получения." — ну так и сообщайте прямо из этой management command. Или как вы по другому собираетесь сообщить? Сохранить в базе что таск завершен а при каждай загрузке страницы юзером трекать базу с задачами (читай лишние дополнительные запросы к базе) и сообщать ему как-то? Получается не просто добавить декоратор чтобы задача выполнилась асинхронно а еще и кусок кода для проверки статусов и репорта юзерам…
> Если речь о маленьких сайтах где практически нет никакой нагрузки мне сложно представить ситуацию когда нужно выполнить какие-то действия асинхронно.

Приведу такой пример, который встречается, к сожалению, довольно часто. Есть интернет-магазин, на который приходят 1,5 посетителя в сутки, и в основное время нагрузка на сайт практически нулевая. А еще у администратора есть поле upload и кнопочка типа «загрузить номеклатуру», с помощью которой он раз в сутки обновляет информацию на сайте. Через upload передается многометровый XML или XLS, который начинает тут же обрабатываться и запихиваться в базу. Даже если этот процесс хорошо оптимизирован, все равно время на обработку легко может превысить максимальное время ожидания ответа веб-сервером, и процесс будет принудительно завершен. Это типичная задача, которая просится в оффлайн, и мы (я имею в виду техподдержку NetAngels) всегда советуем обрабатывать такие задачи как раз с помощью cron-а.

Другая подобная задача возникает уже у меня. Панель управления хостингом не оперирует большим количеством данных, и большинство времени нагрузка на ней тоже близка к никакой. Однако эта панель в ответ на действия пользователя отправляет команды на удаленные серверы (создай сайт, обнови конфиг и т.п.). Если этот сервер чрезмерно нагружен или находится в другом месте и есть проблемы на каналах связи, то работать с панелью станет, как минимум, некомфортно. С другой стороны, если пользователь нажал на кнопочку «изменить пароль ftp», то заставлять ждать его 1-3 минуты, пока задание выполнит cron, тоже негуманно. Поэтому мы вместо этого формируем задания, которые celery выполняет так быстро, как ей это позволяют условия.

> ну так и сообщайте прямо из этой management command

Да, тут я согласен. Действительно, обычно не будет проблемой сообщить что-то клиенту из задачи celery или из management команды. С другой стороны, если возникла необходимость отслеживать состояние задачи из вызывающей функции, то достаточно просто сохранить async_result.task_id и периодически обращаться к задаче, проверяя, не выполнилась ли она. Объект request.session выглядит удобным местом для сохранения таких идентификаторов. В тех случаях, когда задача выполняется за время, сопоставимое с интервалом между запросами клиента, overhead будет минимальным.
«Это типичная задача, которая просится в оффлайн, и мы (я имею в виду техподдержку NetAngels) всегда советуем обрабатывать такие задачи как раз с помощью cron-а.» ну вот и вернулись к крону, причем тут асинхронные задачи?

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

А разве выполнение задач через cron — это не один из способов реализации асинхронных задач? Это был пример того, что даже на ненагруженном сайте не все действия нужно выполнять синхронно, то есть в цикле обработки HTTP-запроса.

И раз уж речь зашла о «ненужном» хостинге. Панель управления управляет не только shared-хостингом. Она работает с разными серверами, в т.ч. и не на нашей площадке, а также с серверами клиентов (которые заказали VDS или разместились на collocation). При всем желании, если канал между площадками просел, или клиент загрузил свой сервер так, что тот еле ворочается, хостер ничем помочь не сможет.

Если вам не нравится такой пример, представьте, что вы парсите выдачу яндекса, обращаетесь к стороннему REST API или ходите по другому сайту, на скорость работы которого никак повлиять не можете. В любом случае, асинхронное выполнение задач, даже если клиент всего один — ваш браузер, будет удачным решением.
Для обработки большого файла крон не нужен. Просто запускаете его обработку в отдельном потоке и возвращаете клиенту «ок, начали обрабатывать, ждите». По итогу письмо на мыло, сообщение в лог, запись в базу, солить по вкусу. Если не хочется с селери заморачиваться. Городить ее ради одной задачи конечно нет смысла.
Простой пример, юзер заливает картинку/видео и надо ее преобразовать в фоне, кидаете задание в celery и забываете про него. С management коммандами будет сильно сложнее
В чем смысл такой статьи? Вы поверхностно перечислили несколько основных моментов, о которых уже есть тысяча таких же статей и раздел «Getting started» документации. Только энтропию увеличиваете.
Я конечно же против увеличения энтропии, но в чём смысл статьи написал в самом начале, и мне не удалось найти актуальной информации по установке и настройке, собранной в одном месте.
Статья отличная, а вы пахоже не заметили что «Данная статья будет полезна прежде всего новичкам»
Полезная штукенция, очень активно используем в Pylons проекте, RabbitMQ как брокер. Но вот почему-то часто появляются зависшие задания / zomby — процессы, ну и по Ctrl+C не всегда возможно прибить. Недостаточно стабильное в общем.

А так да — если даже и не применять активно, то знать о существовании такой штуки совершенно точно нужно, иначе будешь жестко велосипедить.
У меня такое было, если воркер на eventlet или gevent. Точнее, eventlet как-то вообще через раз заводился, а gevent вис под нагрузкой, особенно если в задачах выпадали исключения. Никак в итоге не решил, пока забил (ресурсов сейчас хватает). Видимо, на какой-нибудь торнаде буду отдельно потом неблокирующего работника писать.
Хм… неблокирующий работник? Не представляю зачем… Если только работники занимаются загрузкой веб-страничек…
Ну да, 2 очереди, io (сеть, диск) параллелить неблокирующими работниками, cpu — процессами, схема замечательная, если бы работала)

Сделал пока тоже 2 очереди (под io-задачи и cpu-задачи), но обе просто на процессах. В cpu-очереди смысла особого нет запускать процессов больше, чем ядер у машины, а вот в io — чем больше, тем лучше.
Да, еще интересная возможность — прозрачный запуск задач на разных машинах. Т.е. запускаешь воркеров на 10 серверах и один RabbitMQ брокер — задачи расползаются по кластеру уже самостоятельно.
написали на работе ztask для всего этого, Celery показался слишком громоздким и тяжелым для того, что мы делали. ztask использует 0MQ, если кому интересно — вэлкам github.com/dmgctrl/django-ztask
Кажется вот здесь написано про историю создания ztask: http://www.zeromq.org/story:3
Очень интересно, чуваки решили что слишком много всего устанавливается и надо бы написать что-то своё очень простое, и судя по всему, у них это получилось довольно хорошо.
Кстати там написано что ztask умеет перезагружать изменённые файлы, это действительно так?
ага, мы с Джэйсоном работали над нашим внутренним проектом и нам не хотелось устанавливать больше 1ий сторонней библиотеки, а у Дэйва это было вообще одним из условием сдачи его собственного проекта. Так что, объединив усилия, они написали ztask, который мы теперь почти везде используем для асинхронных задач.
Насчет перезагрузки — да, он умеет перезагружать измененные файлы.
pip install pyzmq
pip install -e git+git@github.com:dmgctrl/django-ztask.git#egg=django_ztask

or

easy_install Celery
easy_install django-celery
django_ztask для нас сторонней библиотекой не является
а django-celery — является?
да, к django-celery мы отношения не имеем, а django_ztask мы писали сами
Понятно. Тогда довольно странное условие здачи проекта :)

Заказчик вкурсе, что Джанго тоже не Вами написано
заказчик — мы, на все вопросы ответил? «вкурсе» кстати пишется раздельно :)
«вкурсе» кстати пишется как хочеш )
ну раз человек спрашивает — почему бы не ответить :) только б спрашивал еще по делу…
не, не на все. даже появилось еще больше.

почему такое странное условие для здачи проекта?

А еще проект надо написать на свеже поставленной убунте не подключенной к интернету ))) Ну чтоб ну вообще… чтоб даже и мысли небыло глянуть в чужой код )
" и нам не хотелось устанавливать больше 1ий сторонней библиотеки, а у Дэйва это было вообще одним из условием сдачи его собственного проекта." — я вот эту вот фразу перетираю
«ну раз человек спрашивает — почему бы не ответить :) только б спрашивал еще по делу…»

Ну… спасибо, что хоть с русским языком помогли.
не обижайся, я просто так и не понял к чему это все было, к тому что нужно было пользоваться celery?
обиделся :(

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

Как может быть условие сдачи проекта — «устанавливать не больше 1ой сторонней библиотеки»

Мне кажется условие «не устанавливать сторонние библиотеки» — само по себе глупо, а не больше одно вообще мне вынесло можечок
ну извини, в следующий раз обязательно спросим твоего совета об использовании сторонних библиотек. судя по твоему блогу, ты ярый приверженец использования готовых библиотек, я ниче против не имею, как говорится «when it makes sense», здесь же условия оказались другими и, мне кажется, время мы потеряли не зря, django_ztask живет и используются не только нами, но и другими людьми.
ох… совета спросим… приверженец готовых библиотек… ну вы даете…

Я пытаюсь выяснить в чем причина. Ну хорошо… Возможные варианты.

— Мы думаем, что в опенсорсе один гавнокод

или

— Мы любим вилосипеды

или

— Мы в проекте не имеем права использовать библиотеки с определенным типом лицензий

или

— У нас такой дурной бюджет, что чем больше мы кодим, тем больше зарабатываем, так что мы еще и свою джангу замутили

или

— Мы считаем что только сцикуны используют готовые библиотеки а настоящие мужики кодят все сами

или

— Нам легче писать свой код, чем поддержить чужой

или

— в следующий раз обязательно спросим твоего совета об использовании сторонних библиотек

а, не стоп, Вы так и ответили… Последнее вычеркиваем… А какой я, кстати, совет дал?
«Мне кажется условие «не устанавливать сторонние библиотеки» — само по себе глупо» звучит как призыв к установке сторонних библиотек. (здесь я игнорирую факт, что по сути вы назвали несколько довольно неглупых людей глупыми, но сами знаете на кого не обижаются...)
1) множество наших проектов opensource, говнокодом их не считаю
2) если бы вы посмотрели код celery и код django_task, вы бы поняли разницу между ними, и не сравнивали бы проект с велосипедом
3) это тоже отчасти правда, но это за рамками NDA
4) клиентов мы не дурим, благо клиенты все люди довольно хорошо разбирающиеся в программировании
5) абсолютно неверно, когда есть смысл — используем готовые библиотеки, но чтобы это делать хорошо, нужно знать на что они способны, их + и — 6) писать свой код почти что всегда легче, чем поддерживать чужой, но не всегда целесообразно
7) уже озвучил
Ух!!! Ну Вы даете!!! )))))))))))))

Вот так и знал, надо было сказать — только без номерков!!! ))))

«Мне кажется условие «не устанавливать сторонние библиотеки» — само по себе глупо» звучит как призыв к установке сторонних библиотек.

Я Вам крайне рекомендую еще раз утричком на свежую голову прочитать то что Вы написали.

Я просто хотел узнать. Какова причина того, что вы не дали добро на использование стороних библиотек.

Фразы между или — это примеры. Возможных варинатов ответов. Это не вопросы к Вам. Там половино вариантов было шуточных. Это я просто Вам показал каким мог быть ответ на мой вопрос. Которким и лаконичным без лишних букв никому не нужных.

Но пожалуй ответ мне уже не нужен, достаточно начитался. Надо работать.

Опять же бложик обновить, раз у меня такой фан появился )))
увольте, когда я увидел количество скобочек — даже продолжать и перечитывать свой же комментарий расхотелось ))))))))))
А django-ztask предоставляет API для периодических тасков?
И интегрируется ли он в админку, как это делает Celery?
не совсем понял вопрос про api для периодических тасков…
в admin ztask напрямую не интегрируется, но это решается довольно просто — добавьте модель Task так, чтобы она была видна в admin.
Кажется я понял вопрос, в модуле decorators у ztask нет periodic_task, то есть периодические задания нельзя создавать.
Не совсем понял, если у меня несколько сайтов, можно ли им пользоваться одной очередью? Или надо для каждого запускать отдельный celeryd?
можно пользоваться одной, по умолчанию так и получится.
Соответственно, надо следить, чтобы таски были названы по-разному? Или оно само разруливается?
Вообще говоря да, но я сейчас понял, что тут есть фундаментальная проблема :) — если в качестве celery backend используется БД, то все сайты должны использоваться одну и ту же БД чтобы все это работало, поэтому в твоем случае стоит наверное переключиться на RabbitMQ для хранения заданий, оно работает по сети и все смогут с ним взаимодействовать. И будет неважно откуда его запускать.
И еще уточни плз. насколько я понял, celery запускается из каталогапроекта. Если проектов больше одного — из какого каталога его запускать?
Есть вопрос. А как из интерфейса вызывать какой-нибудь зарегистрированный таск?
в django в разделе Djcelery / Tasks нет кнопки добавить задачу.
Скорее всего в django-celery нет такой функциональности в базовом комплекте. Но к нему есть различные расширения, например вот это — github.com/mattcaldwell/django-celery-admin-ext, позволяющее запускать периодические задачи прямо из админки.
Sign up to leave a comment.

Articles