Django с Celery: что это такое, как настроить асинхронные задачи

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

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

Чтобы всего этого не случилось, можно использовать специальные инструменты. К примеру, если вы программируете на Python и работаете с Django, одним из возможных решений может стать использование связки Django и Celery.

О том, как она работает и чем может помочь, рассказываем в этой статье.

Для чего подходит связка Django и Celery

Для начала – буквально несколько слов о том, как работает Celery.

Celery – это распределенная система очередей задач, которая позволяет выполнять их в фоновом режиме, то есть отдельно от основного HTTP-запроса, не заставляя пользователя ждать их завершения. Как только вы интегрируете Celery в свое приложение, вы можете отправлять трудоемкие задачи в очередь – чтобы ваше приложение продолжало оперативно реагировать на запросы пользователей. 

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

Подобные возможности помогают выполнять в фоне буквально всё, просто настроив Celery, – примеры использования этой системы могут быть самыми разными: в очереди можно выполнять отправку писем, анализ данных, обработку изображений, генерацию отчетов и т. д.

Чтобы выполнять это и многое другое, потребуется настройка Celery – вот простой алгоритм для примера:

  1. Выбор брокера сообщений

К примеру, можно выбрать Redis (REmote DIctionary Server – сетевое журналируемое хранилище данных типа “ключ” – “значение”). 

В этом случае для установки Redis непосредственно на сервер с Ubuntu необходимо ввести команду:

sudo apt install redis

А для запуска в Docker-контейнере введите команду:

docker run -d -p 6379:6379 redis
  1. Установка пакетов в виртуальную среду

Это можно сделать через стандартные инструменты Python, вот команда для установки пакетов через pip:

pip install celery

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

  1. Создание экземпляра приложения

В папке celery_django необходимо создать файл celery.py, затем открыть его в редакторе и внести следующий фрагмент:

import os

from celery import Celery 

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "celery_django.settings")

app = Celery("celery_django")

app.config_from_object("django.conf:settings", namespace="CELERY")

app.autodiscover_tasks()

При этом в файл __init__.py следует добавить:

from __future__ import absolute_import, unicode_literals

from .celery import app as celery_app

__all__ = ('celery_app',)

Затем в файл settings.py следует внести такие строки:

CELERY_BROKER_URL = "redis://127.0.0.1:6379/0" (эта строка указывает, откуда система распределения процессов будет брать задания)

CELERY_RESULT_BACKEND = "redis://127.0.0.1:6379/0" (эта строка содержит адрес для регистрации результатов) 

В данном примере адреса совпадают, поскольку используется одна и та же база Redis.

  1. Определение задач

Создайте файл tasks.py:

from celery import Celery

app = Celery('tasks', broker='your_broker_url')

@app.task

def add(x, y):

    return x + y.

@app.task – это декоратор из библиотеки Celery в Python, который используется для объявления функции как асинхронной задачи. В данном примере функция складывает значения X и Y.

При этом в Celery предусмотрено несколько методов для асинхронного вызова задач и управления их выполнением – вот основные:

  • task.delay(*args, **kwargs) – короткий способ вызвать задачу асинхронно;
  • chain(tasks) – последовательное выполнение задач (результат одной передается следующей);
  • group(tasks) – параллельное выполнение набора задач, результат возвращается в виде GroupResult;
  • map(func, iterable) – можно использовать для распределения вызовов одной задачи по множеству аргументов.

Сегодня Celery выбирают для самых разных задач: отправки автоматических писем, построения системы адаптации сотрудников, обработки файлов, распределенных вычислений, запуска задач на периодической основе и т. д.

Поделимся примером, как можно запустить Celery для выполнения отправки писем в фоновом режиме.

Как выполнять фоновую отправку писем с Django и Celery

Разберем возможности Celery на конкретном примере для отправки почты с формы – в этом нам поможет репозиторий.

Обратите внимание!
Для повторения процесса потребуются установленные uv, podman и git. Если вы используете другие решения управления зависимостями и/или контейнеризации, потребуется скорректировать команды соответствующим образом.

Итак, клонируем репозиторий и переходим в его директорию:

git clone  https://github.com/stuartmaxwell/django-celery-example.git

Этот репозиторий представляет собой небольшой пример отправки почты с формы, где сам процесс отправки выполняется в фоне воркером Celery и не блокирует работу самого сайта. 

Также нам потребуется локальный контейнер redis – запустим его следующей командой:

podman run -d --name celery-redis -p 127.0.0.1:6379:6379 docker.io/valkey/valkey

Теперь установим зависимости:

uv sync

И укажем в celery_project/settings.py настройки SMTP для отправки:

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.beget.com"  # add your own settings here
EMAIL_PORT = "2525"  # add your own settings here
EMAIL_HOST_USER = "example@betutorial.ru"  # add your own settings here
EMAIL_HOST_PASSWORD = "REDACTED"  # add your own settings here
EMAIL_USE_TLS = False  # add your own settings here
DEFAULT_FROM_EMAIL = "example@betutorial.ru"  # your email address

Выполним миграции:

uv run manage.py migrate

А также запустим процессы сервера Django:

uv run manage.py runserver

И Celery:

uv run celery -A celery_project worker -l info -P solo

После чего перейдем по адресу 127.0.0.1 в браузере и отправим письмо:

контактная форма

В логах сервера Django увидим, что запросы к веб-серверу выполнялись моментально, задержки на отправку не было:

коды ответов

Теперь посмотрим, что происходило за кулисами:

лог django celery

Разберем происходящее по порядку:

  1. Celery ждет появления ивентов в Redis.
  2. Django поместил ивент отправки почты в Redis при получении запроса на форму обратной связи.
  3. Celery получает ивент, пытается отправить письмо, в случае успеха логирует информацию об успешном выполнении.

Для примера – ключи, находящиеся в Redis:

ключи в redis

Осталось лишь проверить, отправлено ли письмо:

contact firm celery

Вот и всё, мы убедились, что письмо отправлено.

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

FAQ о Django и Celery

Какие брокеры сообщений поддерживает Celery? 

В качестве брокера сообщений можно использовать RabbitMQ, Redis, Apache Kafka и т. п.

Подходит ли Celery для других фреймворков, помимо Django?

Celery может использоваться с любыми Python-приложениями и фреймворками (например, Flask, FastAPI или Pyramid).

Можно ли запустить одну задачу многократно одновременно?

Да, каждая отправка в очередь создает отдельный экземпляр задачи, который может выполняться воркерами параллельно.

Как ограничить количество одновременно выполняемых задач?

Это можно сделать с помощью параметров воркера (--concurrency, --autoscale) или настроек очереди и квот (task_annotations, rate_limit).

Заключение

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

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

Надеемся, эта статья была для вас полезна, а длительные задачи не помешают развивать ваш проект 🙂

Если у вас возникли вопросы, свяжитесь с нами удобным для вас способом – и мы обязательно ответим. Также ждем вас в нашем официальном Telegram-канале, а пообщаться на любую тему с коллегами по цеху и сотрудниками Beget вы можете в нашем чате.

5
888