Comments 24
tasktiger — только redis
Huey — только redis
WorQ — только redis
dramatiq — вроде неплохо, но не хватает ряда полезных фич, например, событий.
django-carrot — странная штука, просто обертка над rabbitmq
Ну гуглить я тоже умею, интересно все-таки немного отзывов.
Что значит "явно не prouduction-ready решение" применительно к редису для очередей?
Ну и редис не скалируется по человечески.
К самому редису претензий нет, но использовать его для очереди задач очень странно, на мой взгляд.
Просто на мой взгляд, если где-то меньше десятка, то такой поток вытягивает и кролик на 1 ГБ и со слабым диском.
Из цифр могу сказать что эпизодически ставятся сотни тысяч задач в очередь и этого никто не замечает, если по ошибке поставить 10млн, то да, память кончится. На этот случай есть мониторинг.
Просто меня довольно давно удивляет, что все берут для очередей редис, учитывая, что есть rabbitmq, который и более надежный, и более гибкий, и еще позволяет делать довольно просто отказоустойчивый сетап. Мне казалось, что единственное преимущество редиса — это скорость. Или вы взяли его по другой причине?
Редис будет быстрее рэббита если написать на нём свою очередь, состоящую из пары команд PUSH/BLPOP, в которой ничего больше нет.
Если сделать так чтобы задачи не терялись в случае любых ситуаций (воркер взял задачу и упал), а так же чтобы не было race condition при различных кейзах, а так же возврат результата работы, + статистика, мониторинг, то будет медленнее.
Проблем с надёжностью редиса не вижу. Гибкость как раз больше у редиса (если самому писать под него систему), т.к. это фактически бэкэнд для системы очередей, а не готовая система очередей.
Сложно сравнивать голый рэббит и некую систему очередей X, которая использует Redis.
По самому редису нет никаких нареканий к надёжности, и гибкости. А скорость обычно пострадает при написании системы очередей под него.
При 50000 задач ежесекундно (при условии что воркеры успевают их разгребать) redis в нашем проекте потреляет 80 Мб памяти.
Если очередь перестает помещаться в память, то скорее всего происходит что-то не то: либо задачи накапливаются – что не очень хорошо, либо вы передаете в сообщениях мегабайты данных, чего тоже делать не стоит.
При 50000 задач ежесекундно (при условии что воркеры успевают их разгребать) redis в нашем проекте потреляет 80 Мб памяти.
Хм… это самописное решение или таки celery?
Если очередь перестает помещаться в память, то скорее всего происходит что-то не то: либо задачи накапливаются – что не очень хорошо, либо вы передаете в сообщениях мегабайты данных, чего тоже делать не стоит.
А как насчет таких случаев:
- В сообщениях приходится передавать большие объемы информации. Например, sentry цеплять в сообщение всю инфу по событию, которое приняло по api. Или вы предложили сначала записать это все добро в базу сырым и потом удалить оттуда?
- Задачи выполняются очень долго и все время, пока выполняются висят в очереди (кстати, а у вас так или вы как-то по другому удостоверяетесь в том, что задача не пропала?). К сожалению, те же задачи по интеграции с внешними сервисами могут работать внушительно долго.
- Задачи идут волнами. Например, есть задача "импорт с внешнего источника" и она создает большое количество задач волнами, что как бы приводит к их накоплению.
Ну и да, возвращаясь к редису — вы его как-то кластеризируете или он у вас просто так стоит как единая точка отказа?
Хм… это самописное решение или таки celery?
celery, мы в таски передаем только id-шники, сообщение в таком случае и не должно занять больше 2Кб.
В сообщениях приходится передавать большие объемы информации
Все еще не совсем понятен юзекейс, зачем вы передаете в задачу всю информацию по событию? Событие в sentry все равно посылается асинхронно без задач, а в других случаях разве не нужно сохранять это событие в промежуточный сторадж (база, кэш)? Чтобы потом прочитать его в задаче по ключу? Мы у себя редко передает в параметры задач что-то больше 2Кб. Вся информация, которая нужна в задаче «достается» из каких-либо стораджей.
Задачи выполняются очень долго и все время, пока выполняются висят в очереди (кстати, а у вас так или вы как-то по другому удостоверяетесь в том, что задача не пропала?)
Мы ставим максимальный софт таймаут для некоторых задач на 5 минут, если задача работает больше – значит это ненормально и что-то пошло не так, если внешний сервис лежит, то в задачах у нах предусмотрен exponential backoff и подходящая retry policy. А что значит «не пропала»? Вы имеете ввиду гарантируем ли мы как-то что все задачи действительно выполнены? Нет не гарантируем, да и сам redis такого не гарантирует, но мы знаем об этой особенности, для нашего проекта она пока не критична.
Задачи идут волнами. Например, есть задача «импорт с внешнего источника» и она создает большое количество задач волнами, что как бы приводит к их накоплению.
Буду об этом рассказывать в своем докладе. Мы тоже несколько раз получали task flooding, поэтому поигрались с настройками и написали свой чанкификатор задач, который запускает их определенными порциями и таким образом обеспечивает равномерное накопление задач в очередях и не перегружает внутренние (например базу) и внешние сервисы.
Ну и да, возвращаясь к редису — вы его как-то кластеризируете или он у вас просто так стоит как единая точка отказа?
Пока мы кластеризуем его хуже чем нам хотелось бы, AWS обеспечивает автоматический фейловер в случае если с мастер нодой что-то случилось, но в те несколько секунд пока происходит фейловер – постановка задач фейлится, может быть какие-то окажутся потеряны. Эту проблему мы у себя пока не решили, но можно посмотреть в сторону github.com/dealertrack/celery-redis-sentinel – там есть retry в случае connection error'a.
У вас довольно интересная схема, хотя мне и кажется, что она немного читерская, потому что, исходя из того, что я понял, всю инфу по таске вы храните во внешнем источнике, получается цепочка redis -> storage -> task execution, вместо rabbitmq -> task execution. Мне сложно понять выгоду такой схемы, но выглядит интересно)
А так большое спасибо за такой развернутый ответ, довольно познавательно :)
Касательно кластеризации, немного отвратительный опыт с redis sentinel из-за того, что там нельзя разделить bind/advertise адреса. Из-за этого sentinel совсем у нас не работал.
Зачем тянуть в таск то что можно найти уже в воркере?
Понятно что это не панацея и от всех случаев не спасет, но сама идея весьма продуктивна и работает в продакшене как часы.
А выгода быть может тогда когда данные для успешного выполнения таска могли поменяться.
Если мы передадим в таск сразу готовый набор данных то он окажется устаревшим (Страшно подумать, но например отложенная оплата с баланса в системе или другой кошмар на выбор). И ситуация когда мы в таск передаем только тип операции, id юзера ну и id странзакции..(или id таска в БД) Воркер сам все подтянет, проверит возможность и выполнит или задеклайнит таск…
Celery в нагруженных проектах: немного практики