сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Docker и Kubernetes

Инфраструктура сервиса: очереди задач (Celery)

Очереди задач: долгую работу - в фон

Как только в запросе появляется долгая работа - письмо, отчёт, обработка загрузки - синхронный HTTP перестаёт годиться, и на собесе спрашивают про очереди задач. Ценят понимание at-least-once и идемпотентности: без них фоновые задачи задваивают эффекты.

Типовые формулировки: «что вынести в фон, а что оставить в запросе?», «что такое брокер?», «сколько раз выполнится задача».

Очередь, брокер и что выносить

Очередь задач (Celery, RQ) выносит долгую работу из HTTP-запроса в фон: эндпоинт кладёт задачу и СРАЗУ отвечает, а воркер разбирает её асинхронно. Запросы остаются быстрыми, клиент не ждёт. Брокер (Redis, RabbitMQ) это транспорт: приложение кладёт задачу в очередь, воркеры забирают и исполняют; сам брокер задачи не выполняет, он их только передаёт.

Что выносить: долгое и терпящее задержку - письма, отчёты, обработку загрузок, обращения к медленным API. Что оставить в запросе: то, без чего не собрать ответ - валидацию, чтение нужных данных. Держать долгую операцию прямо в запросе - обрекать клиента на ожидание и таймаут.

// Ментальная граница: в запросе - только то, что нужно для немедленного ответа; всё, что может подождать, в очередь.

очередь задач
вынос долгой работы из запроса в фон (Celery/RQ)
брокер
транспорт задач между приложением и воркерами

At-least-once и идемпотентность

Брокеры дают at-least-once: задача может выполниться ДВАЖДЫ - например, при ретрае после сбоя, когда результат уже был, а подтверждение потерялось. Значит обработчик обязан быть идемпотентным: проверять по ключу или статусу, что эффект ещё не применён, и не задваивать его (не списать деньги, не отправить письмо повторно).

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

// Идемпотентность здесь - та же идея, что Idempotency-Key в API: повтор не должен менять итог против одного выполнения.

at-least-once
задача может доставиться повторно - нужна идемпотентность

Как отвечать: «Сколько раз выполнится задача из очереди?»

По умолчанию - как минимум один раз, но возможно и больше: брокеры дают гарантию at-least-once. Классический сценарий - воркер выполнил задачу, но упал или потерял связь до того, как подтвердил брокеру приём, и тот, не увидев подтверждения, отдаёт задачу снова. Поэтому я не рассчитываю на ровно один раз, а делаю обработчик идемпотентным: перед эффектом проверяю по ключу или статусу, не выполнена ли задача уже, и если да - просто выхожу. Так повторная доставка не задваивает списание или письмо. И на порядок выполнения не закладываюсь - очередь его не гарантирует, воркеры разбирают конкурентно.

Названа гарантия (at-least-once) с конкретным сценарием дубля, дано решение через идемпотентность и добавлена оговорка про порядок - ответ инженера, знающего распределённые грабли.

На чём валят

  • Держать долгую операцию в HTTP-запросе: клиент ждёт и ловит таймаут.
  • Считать задачу выполняемой ровно раз: at-least-once допускает повтор - делай идемпотентной.
  • Ждать от очереди задач строгого порядка выполнения - она его не гарантирует.
  • Путать брокер и воркер: брокер лишь передаёт задачу, исполняет её воркер.

Проверьте себя

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

  1. #task_queues1 / 5
    Зачем в бэкенде очередь задач (Celery, RQ)?
    A)Заменить базу данных для хранения результатов работы приложения
    B)Обеспечить, что задачи выполнятся строго в порядке их создания
    C)Вынести долгую работу из запроса в фон, не заставляя клиента ждать
    D)Ускорить сам код задачи, распараллелив его по ядрам процессора
    показать ответ и разбор
    +C)Вынести долгую работу из запроса в фон, не заставляя клиента ждать

    // разбор: Долгие операции (отправка писем, генерация отчёта, обработка видео, тяжёлый счёт) нельзя держать в HTTP-запросе — клиент ждёт и таймауты. Их кладут в очередь, воркер разбирает асинхронно, а эндпоинт сразу отвечает «принято». Так запросы остаются быстрыми, а тяжёлое уезжает в фон.

  2. #task_queues2 / 5
    Какую роль играет брокер (Redis, RabbitMQ) при работе Celery?
    A)Хранит исходный код задач и раздаёт его воркерам при старте
    B)Исполняет сами задачи вместо воркеров, чтобы разгрузить приложение
    C)Транспорт задач: держит очередь между продюсером и воркерами
    D)Балансирует HTTP-трафик между инстансами веб-приложения по ролям
    показать ответ и разбор
    +C)Транспорт задач: держит очередь между продюсером и воркерами

    // разбор: Брокер — транспорт сообщений: приложение кладёт задачу (имя + аргументы) в очередь брокера, а воркеры Celery её оттуда забирают и исполняют. Redis прост и быстр, RabbitMQ богаче по гарантиям доставки и маршрутизации. Сам брокер задачи не выполняет — он лишь надёжно переносит их до воркеров.

  3. #task_queues3 / 5
    Celery-задача может выполниться дважды (ретрай, at-least-once). Каким должен быть её код?
    A)Обёрнутым в одну транзакцию, и тогда повтор станет невозможен сам собой
    B)Идемпотентным: повтор не должен задваивать эффект
    C)Строго однопоточным, ведь при повторе виноват параллелизм воркеров
    D)Максимально быстрым, чтобы просто не успеть выполниться повторно
    показать ответ и разбор
    +B)Идемпотентным: повтор не должен задваивать эффект

    // разбор: Брокеры обычно дают at-least-once: при сбое подтверждения задача доставляется повторно, поэтому она может выполниться дважды. Значит, эффект должен быть идемпотентным — проверять, не сделано ли уже (по ключу/статусу), прежде чем списывать деньги или слать письмо. Полагаться на «ровно один раз» нельзя.

  4. #task_queues4 / 5
    Что обычно выносят в фоновые задачи, а что оставляют в запросе?
    A)В фон выносят вообще всё, чтобы запросы отвечали мгновенно
    B)Ничего: фоновые задачи усложняют систему и обычно лишние
    C)В фон — долгое и терпящее задержку; в запросе — быстрое и нужное сразу
    D)В фон — только чтение из базы, а запись держат в запросе
    показать ответ и разбор
    +C)В фон — долгое и терпящее задержку; в запросе — быстрое и нужное сразу

    // разбор: В фон уходит то, что долго и терпит задержку: рассылки, генерация отчётов/тумбнейлов, обработка загрузок, интеграции с медленными внешними API. В запросе оставляют то, без результата чего клиенту нельзя ответить: валидацию, авторизацию, чтение для рендера. Критерий — длительность и срочность, а не тип операции.

  5. #task_queues5 / 5
    Зачем выносить отправку письма в очередь задач, а не слать прямо в обработчике запроса?
    A)Чтобы письма отправлялись строго в том же порядке, в котором пришли запросы
    B)Чтобы эндпоинт ответил сразу, а долгую работу воркер сделал в фоне
    C)Чтобы зашифровать содержимое писем перед их отправкой пользователям
    D)Чтобы полностью отказаться от базы данных, храня все задачи только в очереди
    показать ответ и разбор
    +B)Чтобы эндпоинт ответил сразу, а долгую работу воркер сделал в фоне

    // разбор: Отправка письма (или обработка загрузки, генерация отчёта) — долгая и терпящая задержку операция. Делать её синхронно в обработчике значит заставить клиента ждать и держать веб-воркер занятым. Очередь задач принимает задание, эндпоинт сразу отвечает, а фоновый воркер выполняет работу асинхронно. Запросы остаются быстрыми, а тяжёлое уходит с горячего пути.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.