Отправка письма, генерация PDF, обработка загруженного изображения, рассылка — такую работу нельзя делать прямо в обработчике HTTP-запроса. Пользователь ждёт ответа, веб-воркер занят и не обслуживает других, а долгая операция упирается в таймаут reverse proxy. Решение — фоновые задачи: приложение кладёт задание в очередь и сразу отвечает, а выполняет его отдельный процесс. Разберём, из чего это состоит и чем запускать.
Из чего состоит фоновая обработка
В любой реализации три части:
- Producer — приложение, которое кладёт задачу в очередь (например, в обработчике запроса).
- Брокер — хранилище очереди. Чаще всего Redis, для сложных сценариев — RabbitMQ.
- Worker — отдельный процесс, который забирает задачи из очереди и выполняет.
Ключевое здесь — worker это отдельный долгоживущий процесс, не часть веб-сервера. Его нужно запускать и держать запущенным наравне с приложением (под супервизором — см. как держать процесс 24/7).
RQ — простой путь
Для большинства задач достаточно RQ: очередь поверх Redis с минимумом кода. Приложение кладёт задачу:
from redis import Redis
from rq import Queue
queue = Queue(connection=Redis())
queue.enqueue(send_email, user_id, subject) # вернётся мгновенно
А воркер запускается отдельной командой и сам разбирает очередь:
rq worker
Понятная модель, легко отлаживать, легко понять, что происходит. Для пет-проектов и большинства продуктов этого хватает.
Celery — мощнее, но сложнее
Celery нужен, когда требуется больше: несколько очередей с приоритетами, маршрутизация задач, хранение результатов, расписание, гибкие ретраи. Цена — выше порог входа и больше движущихся частей.
from celery import Celery
app = Celery("tasks", broker="redis://localhost:6379/0")
@app.task
def send_email(user_id, subject):
...
Воркер запускается так:
celery -A tasks worker
Если очереди простые, Celery часто оказывается избыточным. Брать его стоит осознанно, под конкретные требования, а не по умолчанию.
Периодические задачи
Задачи по расписанию (ночной отчёт, очистка) делают одним из трёх способов:
- Celery Beat — планировщик Celery, кладёт задачи в очередь по расписанию.
- RQ Scheduler — аналог для RQ.
- Системный cron, который дёргает management-команду или скрипт.
Важно не запускать периодическую задачу в нескольких экземплярах: два Beat-процесса поставят задачу дважды.
RQ или Celery: как выбрать
Простое правило — начинать с RQ и переходить на Celery, когда упираешься в его ограничения. RQ подходит, если задачи однотипные, брокер — Redis, а нужно «положить в очередь и выполнить». Celery оправдан, когда появляются несколько очередей с приоритетами, маршрутизация задач по разным воркерам, хранение и связывание результатов, сложное расписание или работа через RabbitMQ. Брать Celery «на вырост» под простые задачи — значит платить сложностью без отдачи.
За очередью важно наблюдать: у RQ для этого есть rq-dashboard, у Celery — Flower. Они показывают, что в очереди, что выполняется и что упало, — без такой панели ошибки в фоне легко не заметить.
Что важно в проде
- Воркер — под супервизором. Упавший воркер должен подниматься сам, иначе очередь молча копится.
- Идемпотентность. Очереди дают доставку «хотя бы один раз» (at-least-once): при сбое задача может выполниться повторно. Списание денег или отправка письма должны быть устроены так, чтобы повтор не навредил, — через проверку «уже сделано».
- Ретраи с задержкой. Внешний сервис временно недоступен — задачу стоит повторить с нарастающей паузой, а не уронить.
- Видимость. Нужно видеть упавшие задачи (failed registry в RQ, мониторинг в Celery), иначе ошибки теряются.
- Сохранность очереди. Брокер должен переживать перезапуск: у Redis для этого включают persistence (RDB или AOF), иначе при рестарте все невыполненные задачи пропадут.
Частые грабли
- Воркер не видит конфигурацию. Процесс запущен без тех же переменных окружения, что и приложение, — и падает на подключении к базе.
- Долгая задача блокирует воркер. Один воркер обрабатывает задачи по очереди; для параллелизма поднимают несколько воркеров или повышают concurrency.
- Потеря задач при падении брокера. Redis без persistence теряет очередь при перезапуске.
- Двойное выполнение. Без идемпотентности повторно выполненная задача шлёт второе письмо или делает второе списание.
- Раздувшаяся очередь. Воркер упал и не поднялся — задачи копятся, пользователи не получают результат, а заметно это становится не сразу.
Чеклист
- Тяжёлые операции вынесены из обработчика запроса в фоновую задачу.
- Выбран инструмент под задачу: RQ для простого, Celery — под расписание, приоритеты и результаты.
- Воркер запущен отдельным процессом под супервизором с автоперезапуском.
- Задачи идемпотентны, настроены ретраи с задержкой.
- У брокера (Redis) включён persistence.
- Есть способ видеть упавшие задачи.
Фоновая обработка добавляет к приложению ещё два постоянно живущих компонента — брокер и воркер, — которые тоже нужно где-то держать и обслуживать. На Hostim managed Redis (или Dragonfly) и Background Worker поднимаются рядом с приложением в одном проекте: воркер держится платформой как отдельный процесс, а очередь — на managed-брокере с бэкапами, без ручной настройки persistence и супервизора.