Asynchronous Tasks With Django and Celery

Оглавление

Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой Real Python. Посмотрите его вместе с письменным руководством, чтобы углубить свое понимание: Создание асинхронных задач с помощью Celery и Django

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

К концу этого урока вы поймете, что:

  • Celery - это распределенная очередь задач, которая обрабатывает задачи вне основного потока приложений Django.
  • Celery в Python превосходен в разгрузке работы и независимом планировании задач.
  • Использование Celery в Django помогает поддерживать оперативность приложения при выполнении трудоемких задач.
  • Настройка Celery в Django включает в себя настройку посредника сообщений и определение задач.
  • Для запуска задачи в Celery требуется вызвать задачу с помощью .delay() или .apply_async().
  • Celery не является очередью сообщений, но использует посредника сообщений, такого как Redis, для обмена сообщениями.

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

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

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

Основы Python Celery

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

Примечание: Celery прекратил поддержку Windows в версии 4, поэтому, хотя вы все еще можете чтобы заставить его работать в Windows, вам лучше использовать другую очередь задач, например, huey или Dramatiq..

В этом руководстве вы сосредоточитесь на использовании Celery в системах UNIX, поэтому, если вы пытаетесь настроить распределенную очередь задач в Windows, то это руководство может вам не подойти.

Чтобы получать задания из вашей программы и отправлять результаты на сервер, Celery требуется посредник сообщений для связи. Redis и RabbitMQ - это два брокера сообщений, которые разработчики часто используют вместе с Celery.

В этом руководстве вы будете использовать Redis в качестве посредника сообщений. Чтобы испытать себя, вы можете отклониться от инструкций и вместо этого использовать RabbitMQ в качестве посредника сообщений.

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

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

Однако часто бывает полезно вести учет результатов выполнения всех задач, особенно если вы распределяете задачи по нескольким очередям. Для сохранения информации о результатах выполнения задач вам нужна серверная часть базы данных.

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

В рамках этого руководства вы не будете выполнять никакой работы с записанными результатами задания. Однако в качестве следующего шага вы могли бы проверить результаты с помощью интерфейса командной строки Redis (CLI) или перенести информацию на специальную страницу в вашем проекте Django.

Зачем использовать сельдерей?

Есть две основные причины, по которым большинство разработчиков хотят начать использовать Celery:

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

Сельдерей - отличный выбор для обоих этих вариантов использования. Он определяет себя как “очередь задач, ориентированная на обработку в режиме реального времени, а также поддерживающая планирование задач” (Источник).

Несмотря на то, что обе эти функции являются частью Celery, они часто рассматриваются отдельно:

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

Сотрудники Celery являются основой Celery. Даже если вы планируете выполнять повторяющиеся задачи с помощью Celery beat, сотрудник Celery выполнит ваши инструкции и выполнит их в назначенное время. Что Celery beat добавляет в эту смесь, так это основанный на времени планировщик для работников Celery.

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

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

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

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

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

  • Обработка изображений: Возможно, вы захотите изменить размер изображений аватаров, которые загружают пользователи, или применить некоторую кодировку ко всем изображениям, которыми пользователи могут делиться на вашей платформе. Обработка изображений часто является ресурсоемкой задачей, которая может замедлить работу вашего веб-приложения, особенно если вы обслуживаете большое сообщество пользователей.

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

  • Вызовы API и другие веб-запросы: Если вам нужно отправлять веб-запросы для предоставления сервиса, который предлагает ваше приложение, вы можете столкнуться с непредвиденным временем ожидания. Это справедливо как для запросов API с ограниченной скоростью, так и для других задач, таких как очистка веб-страниц. Часто бывает лучше передать эти запросы другому процессу.

  • Анализ данных: Обработка данных, как известно, требует больших ресурсов. Если ваше веб-приложение анализирует данные ваших пользователей, вы быстро заметите, что ваше приложение перестает отвечать на запросы, если вы выполняете всю работу правильно в Django.

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

  • Создание отчетов: Если вы используете приложение, которое позволяет пользователям создавать отчеты на основе предоставленных ими данных, вы заметите, что создание PDF-файлов происходит не мгновенно. Для пользователей будет удобнее, если вы позволите Celery обрабатывать это в фоновом режиме, а не замораживать веб-приложение до тех пор, пока отчет не будет готов к загрузке.

Основные настройки для всех этих различных вариантов использования будут одинаковыми. Как только вы поймете, как передавать процессы, требующие больших вычислений или времени, в распределенную очередь задач, вы освободите Django для обработки цикла HTTP-запроса-ответа .

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

Интеграция Celery с Django

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

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

Настройка приложения для обратной связи

Для начала загрузите исходный код предоставленного приложения для обратной связи:

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

Распакуйте загруженный файл и с помощью терминала перейдите в каталог source_code_initial/, где вы должны увидеть стандартную структуру папок проекта Django:

 

source_code_initial/
│
├── django_celery/
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
│
├── feedback/
│   │
│   ├── migrations/
│   │   └── __init__.py
│   │
│   ├── templates/
│   │   │
│   │   └── feedback/
│   │       ├── base.html
│   │       ├── feedback.html
│   │       └── success.html
│   │
│   │
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── forms.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
│
├── .gitignore
├── manage.py
└── requirements.txt

Подтвердите, что вы находитесь внутри source_code_initial/, затем создайте и активируйте виртуальную среду:

$ python -m venv venv
$ source venv/bin/activate
(venv) $

Как только ваша виртуальная среда будет активна, вы можете установить Django:

(venv) $ python -m pip install django

Завершите локальную настройку приложения Django, выполнив миграцию и запустив сервер разработки:

(venv) $ python manage.py migrate
(venv) $ python manage.py runserver

Теперь вы можете открыть свой браузер и перейти на домашнюю страницу приложения по адресу https://localhost:8000, где вас встретит дружелюбная форма обратной связи:

A blank feedback form

Однако в настоящее время эта форма обратной связи выглядит только <<дружелюбно. Заполните форму и отправьте отзыв. Представьте, что один из пользователей вашего веб-приложения столкнулся бы с подобной ситуацией:

После нажатия кнопки Отправить приложение зависает. Вы можете видеть, как на вкладке браузера вращается маленький символ spinner, но страница не отвечает, и вы по-прежнему можете видеть всю информацию, которую вы ввели в форму.

Джанго требуется способ слишком много времени, чтобы обработать форму и перенаправить вас на страницу успешного завершения!

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

Причина, по которой он зависает на так долго, заключается в скрытом time.sleep() вызове .send_email(), который имитирует трудоемкую задачу, которая может быть связано с отправкой электронной почты.

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

Примечание: Замените вызов time.sleep() любым трудоемким процессом, который вам необходимо выполнить в вашем веб-приложении для обслуживания ваших пользователей.

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

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

Установите Celery в качестве очереди задач

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

Первым шагом в интеграции Celery в ваше приложение Django является установка пакета Celery в вашу виртуальную среду:

(venv) $ python -m pip install celery

Однако просто установить Celery недостаточно. Если вы попытаетесь запустить очередь задач, вы заметите, что Celery сначала запускается нормально, но затем выдает сообщение об ошибке, указывающее на то, что Celery не может найти посредника сообщений:

(venv) $ python -m celery worker
[ERROR/MainProcess] consumer: Cannot connect to
⮑ amqp://guest:**@127.0.0.1:5672//: [Errno 61] Connection refused.
Trying again in 2.00 seconds... (1/100)

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

Примечание: Вы можете заметить синтаксис, похожий на URL, в целевом объекте, к которому пытается подключиться Celery. Название протокола, amqp, расшифровывается как Протокол расширенной очереди сообщений и является протоколом обмена сообщениями, который использует Celery. Наиболее известным проектом, который изначально реализует AMQP, является RabbitMQ, но Redis также может взаимодействовать с помощью этого протокола.

Перед использованием Celery вам необходимо установить посредника сообщений и определить проект в качестве производителя сообщений. В вашем случае производителем является ваше приложение Django, а посредником сообщений будет Redis.

Установите Redis в качестве брокера Celery и серверной части базы данных

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

Вернитесь к своему терминалу и установите Redis в свою систему:

(venv) $ sudo apt update
(venv) $ sudo apt install redis
(venv) $ brew install redis

Для успешного выполнения этой команды у вас должен быть установлен Homebrew.

После завершения установки вы можете запустить сервер Redis, чтобы убедиться, что все заработало. Откройте новое окно терминала для запуска сервера:

$ redis-server

Это окно будет вашим выделенным окном терминала для Redis. Держите его открытым до конца этого урока.

Примечание: При запуске redis-server запускается сервер Redis. Вы запускаете Redis как процесс, независимый от Python, поэтому вам не нужно активировать свою виртуальную среду при ее запуске.

После запуска redis-server На вашем терминале отобразится логотип Redis в формате ASCII, а также несколько сообщений журнала запуска. В последнем сообщении журнала будет указано, что Redis готов принимать подключения.

Чтобы проверить, работает ли связь с сервером Redis, запустите Redis CLI в другом новом окне терминала:

$ redis-cli

Как только запрос изменится, вы можете ввести ping и нажать Введите, затем дождитесь ответа от Redis:

127.0.0.1:6379> ping
PONG
127.0.0.1:6379>

После запуска командной строки Redis с помощью redis-cli вы отправили слово ping на сервер Redis, который ответил авторитетным PONG. Если вы получили этот ответ, значит, ваша установка Redis прошла успешно, и Celery сможет взаимодействовать с Redis.

Выйдите из командной строки Redis, нажав Ctrl+C, прежде чем переходить к следующему шагу.

Далее, вам понадобится клиент на Python для взаимодействия с Redis. Убедитесь, что вы находитесь в окне терминала, где ваша виртуальная среда все еще активна, а затем установите redis-py:

(venv) $ python -m pip install redis

Эта команда не устанавливает Redis в вашей системе, а только предоставляет интерфейс Python для подключения к Redis.

Примечание: Вам потребуется установить Redis в вашей системе и redis-py в вашей виртуальной среде Python, чтобы вы могли работать с Redis из ваших программ на Python.

После завершения обеих установок вы успешно настроили посредника сообщений. Однако вы еще не подключили свой producer к Celery.

Если вы попытаетесь запустить Celery сейчас и указать название приложения-производителя, передав параметр -A вместе с названием вашего приложения Django (django_celery), вы столкнетесь с еще одной ошибкой:

(venv) $ python -m celery -A django_celery worker
...
Error: Invalid value for '-A' / '--app':
Unable to load celery application.
Module 'django_celery' has no attribute 'celery'

Пока что ваша распределенная очередь задач не может получать сообщения из вашего приложения Django, поскольку в вашем проекте Django не настроено приложение Celery.

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

Добавьте сельдерей в свой проект на Django

Последняя часть головоломки - это подключение приложения Django в качестве средства создания сообщений к вашей очереди задач. Вы начнете с предоставленного кода проекта, так что продолжайте и загрузите его, если вы еще этого не сделали:

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

Как только код проекта будет загружен на ваш компьютер, перейдите в папку django_celery management app и создайте новый файл с именем celery.py:

django_celery/
├── __init__.py
├── asgi.py
├── celery.py
├── settings.py
├── urls.py
└── wsgi.py

Celery рекомендует использовать этот модуль для определения экземпляра приложения Celery. Откройте файл в вашем любимом текстовом редакторе или IDE и добавьте необходимый код:

django_celery/celery.py

 1import os
 2from celery import Celery
 3
 4os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_celery.settings")
 5app = Celery("django_celery")
 6app.config_from_object("django.conf:settings", namespace="CELERY")
 7app.autodiscover_tasks()

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

  • Строка 1: Вы импортируете встроенный модуль os, с которым вы, возможно, знакомы по работе с файлами. Вы будете использовать его в строке 4, чтобы задать переменную окружения.

  • Строка 2: Вы импортируете Celery из пакета celery. Вы будете использовать его в строке 5 для создания своего экземпляра приложения Celery.

  • Строка 4: Вы используете .setdefault() из os.environ чтобы гарантировать, что модуль settings.py вашего проекта Django доступен с помощью ключа "DJANGO_SETTINGS_MODULE".

  • Строка 5: Вы создаете Экземпляр приложения Celery и указываете имя основного модуля в качестве аргумента. В контексте вашего приложения Django основным модулем является приложение Django, которое содержит celery.py, поэтому вы передаете "django_celery".

  • Строка 6: Вы определяете файл настроек Django как файл конфигурации для Celery и предоставляете пространство имен "CELERY". Вам нужно будет указать значение пространства имен, за которым следует символ подчеркивания (_), для каждой переменной конфигурации, относящейся к Celery. Вы могли бы определить другой файл настроек, но сохранение конфигурации Celery в файле настроек Django позволяет вам придерживаться единого центрального места для конфигураций.

  • Строка 7: Вы указываете экземпляру приложения Celery автоматически находить все задачи в каждом приложении вашего проекта Django. Это работает до тех пор, пока вы придерживаетесь структуры повторно используемых приложений и определяете все задачи Celery для приложения в специальном модуле tasks.py. Вы создадите и заполните этот файл для своего django_celery приложения, когда переработаете код отправки электронной почты позже.

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

django_celery/settings.py

# ...

# Celery settings
CELERY_BROKER_URL = "redis://localhost:6379"
CELERY_RESULT_BACKEND = "redis://localhost:6379"

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

Примечание: Эти URL-адреса также могут указывать на разные серверы и службы. Например, вы могли бы использовать RabbitMQ в качестве посредника сообщений, а Redis - в качестве серверной части результатов:

CELERY_BROKER_URL = "amqp://myuser:mypassword@localhost:5672/myvhost"
CELERY_RESULT_BACKEND = "redis://localhost:6379"

Когда вы запустите свое приложение в рабочую среду, вы замените эти URL-адреса на рабочие адреса каждой службы.

Обратите внимание на CELERY_ пространство имен в начале этих переменных настройки. Вам нужно добавить это из-за аргумента namespace="CELERY", который вы передали в app.config_from_object() в строке 8 celery.py.

На данный момент вы почти закончили интегрировать Celery в свое веб-приложение. Последнее дополнение относится к __init__.py вашего приложения для управления:

django_celery/
├── __init__.py
├── asgi.py
├── celery.py
├── settings.py
├── urls.py
└── wsgi.py

Откройте файл в текстовом редакторе. В проекте Django по умолчанию в каждой папке приложения есть файл __init__.py, который помогает пометить его как модуль. По умолчанию файл пуст, но вы можете добавить код, чтобы повлиять на поведение при импорте.

Чтобы убедиться, что ваше приложение Celery загружается при запуске Django, вам следует добавить его в __all__:

django_celery/__init__.py

from .celery import app as celery_app

__all__ = ("celery_app",)

Загрузка приложения Celery при запуске Django гарантирует, что @shared_task декоратор будет использовать его правильно. Вы узнаете больше о @shared_task в следующем разделе.

Время протестировать настройки! Помните, что для выполнения процесса, который вы настраиваете, требуется одновременный запуск как минимум трех служб:

  1. Производитель: Ваше приложение для Django
  2. Посредник обмена сообщениями: Сервер Redis
  3. Пользователь: Ваше приложение Celery

Поскольку вы используете Redis, вы получите серверную часть базы данных в качестве бонуса, не запуская другую службу.

Откройте три отдельных окна терминала и запустите все программы, если они еще не запущены.

Запустите свое веб-приложение с помощью сервера разработки Django в первом окне:

(venv) $ python manage.py runserver

Затем запустите сервер Redis во втором окне терминала, если вы остановили его ранее:

$ redis-server

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

Примечание: Вы можете получить сообщение об ошибке, если сервер Redis все еще работает в фоновом режиме. Если это так, то вам нужно будет вызвать SHUTDOWN перед выполнением redis-server.

Наконец, теперь вы также можете корректно запускать Celery, не выдавая сообщения об ошибке:

(venv) $ python -m celery -A django_celery worker

При запуске Celery с помощью этой команды вы указываете имя модуля, содержащего экземпляр вашего приложения Celery, "django_celery" для -A.

Примечание: Хотя сообщение об ошибке, которое вы видели при запуске Celery до установки Redis, исчезло, вы все еще можете видеть предупреждение, связанное с настройкой DEBUG в Django. Вы можете проигнорировать это предупреждение для данного примера приложения, но вам всегда следует установить для DEBUG значение False перед развертыванием сайта в рабочей среде.

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

В следующем разделе вы проведете рефакторинг .send_email(), чтобы вызвать асинхронную задачу Celery вместо синхронной обработки отправки электронной почты в Django.

Асинхронно обрабатывать Рабочие Нагрузки с Помощью Celery

Вы успешно собрали элементы головоломки, необходимые для выполнения асинхронных задач с помощью Django, Redis и Celery. Но на данный момент вы еще не определили ни одной задачи для передачи в Celery.

Ваш последний шаг к интеграции Celery с Django и передаче работы в распределенную очередь задач Celery - это преобразовать функциональность отправки электронной почты в задачу Celery.

Вернитесь к синхронному коду

На данный момент ваш код определяет функциональность отправки электронной почты в .send_email() из FeedbackForm в forms.py:

feedback/forms.py

 1from time import sleep
 2from django.core.mail import send_mail
 3from django import forms
 4
 5class FeedbackForm(forms.Form):
 6    email = forms.EmailField(label="Email Address")
 7    message = forms.CharField(
 8        label="Message", widget=forms.Textarea(attrs={"rows": 5})
 9    )
10
11    def send_email(self):
12        """Sends an email when the feedback form has been submitted."""
13        sleep(20)  # Simulate expensive operation(s) that freeze Django
14        send_mail(
15            "Your Feedback",
16            f"\t{self.cleaned_data['message']}\n\nThank you!",
17            "support@example.com",
18            [self.cleaned_data["email_address"]],
19            fail_silently=False,
20        )

Вы определяете .send_email() в строке 11. Метод имитирует дорогостоящую операцию, которая приведет к зависанию вашего приложения на двадцать секунд с вызовом sleep() в строке 13. В строках с 14 по 20 вы создаете электронное письмо, которое будете отправлять с помощью удобного для вас Django send_mail(),, которое вы импортировали в строке 2.

Вам также необходимо вызвать .send_email() при успешной отправке формы, и вы задаете это в .form_valid() из views.py:

feedback/views.py

 1from feedback.forms import FeedbackForm
 2from django.views.generic.edit import FormView
 3from django.views.generic.base import TemplateView
 4
 5class FeedbackFormView(FormView):
 6    template_name = "feedback/feedback.html"
 7    form_class = FeedbackForm
 8    success_url = "/success/"
 9
10    def form_valid(self, form):
11        form.send_email()
12        return super().form_valid(form)
13
14class SuccessView(TemplateView):
15    template_name = "feedback/success.html"

В строке 10 определяется .form_valid(), который FeedbackFormView автоматически вызывает при успешной отправке формы. В строке 11 вы, наконец, вызываете .send_email().

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

Реорганизовать код как задачу Celery

Чтобы app.autodiscover_tasks() работал так, как описано, вам необходимо определить свои задачи Celery в отдельном модуле tasks.py внутри каждого приложения вашего проекта Django.

Примечание: В этом примере у вас есть только одно приложение. В крупных проектах Django, скорее всего, будет больше приложений. Если вы будете придерживаться стандартной настройки, то создадите файл tasks.py для каждого приложения и сохраните в нем задачи приложения Celery в этом файле.

Создайте новый файл с именем tasks.py в вашем приложении feedback/:

feedback/
│
├── migrations/
│   └── __init__.py
│
├── templates/
│   │
│   └── feedback/
│       ├── base.html
│       ├── feedback.html
│       └── success.html
│
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── models.py
├── tasks.py
├── tests.py
├── urls.py
└── views.py

В этом файле вы определяете новую функцию, которая будет обрабатывать логику отправки электронной почты. Извлеките код из .send_mail() в forms.py и используйте его в качестве основы для создания send_feedback_email_task() в tasks.py:

feedback/tasks.py

 1from time import sleep
 2from django.core.mail import send_mail
 3
 4def send_feedback_email_task(email_address, message):
 5    """Sends an email when the feedback form has been submitted."""
 6    sleep(20)  # Simulate expensive operation(s) that freeze Django
 7    send_mail(
 8        "Your Feedback",
 9        f"\t{message}\n\nThank you!",
10        "support@example.com",
11        [email_address],
12        fail_silently=False,
13    )

Не забудьте добавить необходимые импортные данные, как показано в строках 1 и 2.

До сих пор вы в основном копировали код из .send_mail() в send_feedback_email_task(). Вы также немного отредактировали определение функции, добавив два параметра в строку 4. Вы используете эти параметры в строках 9 и 11, чтобы заменить значения, которые вы ранее извлекли из .cleaned_data, на .send_mail(). Это изменение необходимо, поскольку у вас нет доступа к этому атрибуту экземпляра в вашей новой функции.

Кроме этого, send_feedback_email_task() выглядит так же, как и .send_email(). Сельдерей еще даже не был задействован!

Чтобы преобразовать эту функцию в задачу для сельдерея, все, что вам нужно сделать, это добавить в нее @shared_task,, который вы импортируете из celery:

feedback/tasks.py

from time import sleep
from django.core.mail import send_mail
from celery import shared_task

@shared_task()
def send_feedback_email_task(email_address, message):
    """Sends an email when the feedback form has been submitted."""
    sleep(20)  # Simulate expensive operation(s) that freeze Django
    send_mail(
        "Your Feedback",
        f"\t{message}\n\nThank you!",
        "support@example.com",
        [email_address],
        fail_silently=False,
    )

После импорта shared_task() из celery и оформления send_feedback_email_task() с его помощью вы завершите необходимые изменения кода в этом файле.

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

Если ваш разработчик - приложение для Django, то вам нужно использовать @shared_task декоратор для настройки задачи, которая позволяет повторно использовать ваши приложения.

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

Вернитесь к forms.py, откуда вы взяли код для отправки электронной почты, и преобразуйте .send_email() так, чтобы он вызывал send_feedback_email_task():

feedback/forms.py

 1# Removed: from time import sleep
 2# Removed: from django.core.mail import send_mail
 3from django import forms
 4from feedback.tasks import send_feedback_email_task
 5
 6class FeedbackForm(forms.Form):
 7    email = forms.EmailField(label="Email Address")
 8    message = forms.CharField(
 9        label="Message", widget=forms.Textarea(attrs={"rows": 5})
10    )
11
12    def send_email(self):
13        send_feedback_email_task.delay(
14            self.cleaned_data["email"], self.cleaned_data["message"]
15        )

Вместо того, чтобы обрабатывать логику кода отправки электронной почты в .send_email(), вы переместили ее в send_feedback_email_task() в tasks.py. Это изменение означает, что вы также можете удалить устаревшие инструкции по импорту в строках 1 и 2.

Теперь вы импортируете send_feedback_email_task() из feedback.tasks в строке 4.

В строке 13 вы вызываете .delay() на send_feedback_email_task() и передаете ему данные отправленной формы, полученные из .cleaned_data в качестве аргументов в строке 14.

Примечание: Вызов .delay() - это самый быстрый способ отправить сообщение о задаче в Celery. Этот метод является сокращением к более мощному .apply_async(), который дополнительно поддерживает параметры выполнения для точной настройки вашего сообщения о задаче.

Используя .apply_async(), ваш вызов для достижения того же, что и выше, был бы немного более подробным:

send_feedback_email_task.apply_async(
    args=[
        self.cleaned_data["email"],
        self.cleaned_data["message"],
    ]
)

В то время как .delay() является лучшим выбором в таком простом сообщении о задаче, как это, вы сможете воспользоваться многими вариантами выполнения с помощью .apply_async(), такими как countdown и retry..

После внесения этих изменений в tasks.py и forms.py рефакторинг завершен! Основная часть работы по выполнению асинхронных задач с помощью Django и Celery заключается в настройке, а не в самом коде, который вам нужно написать.

Но работает ли это? Продолжают ли приходить электронные письма и продолжает ли ваше приложение Django реагировать на них?

Протестируйте свою асинхронную задачу

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

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

Вы создали задачу, о которой не знает рабочий, запущенный вами ранее, поэтому вам нужно перезапустить рабочий. Откройте окно терминала, в котором вы запускаете Celery worker, и остановите выполнение, нажав Ctrl+C.

Затем перезапустите worker той же командой, которую вы использовали ранее, и добавьте -l info, чтобы установить уровень регистрации в info:

(venv) $ python -m celery -A django_celery worker -l info

Установка параметра -l на info означает, что вы увидите дополнительную информацию, напечатанную на вашем терминале. При запуске Celery отображает все задачи, которые он обнаружил в разделе [tasks]:

[tasks]
  . feedback.tasks.send_feedback_email_task

Этот вывод подтверждает, что Celery зарегистрировался send_feedback_email_task() и готов обрабатывать входящие сообщения, связанные с этой задачей.

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

Асинхронный

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

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

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

[INFO/MainProcess] celery@Martins-MBP.home ready.
[INFO/MainProcess] Task feedback.tasks.send_feedback_email_task
⮑ [a5054d64-5592-4347-be77-cefab994c2bd] received
[WARNING/ForkPoolWorker-7] Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: Your Feedback
From: support@example.com
To: martin@realpython.com
Date: Tue, 12 Jul 2025 14:49:23 -0000
Message-ID: <165763736314.3405.4812564479387463177@martins-mbp.home>

        Great!

Thank you!
[WARNING/ForkPoolWorker-7] -----------------------------------------
[INFO/ForkPoolWorker-7] Task feedback.tasks.send_feedback_email_task
⮑ [a5054d64-5592-4347-be77-cefab994c2bd] succeeded in 20.078754458052572s:
⮑ None

Потому что вы запустили свой Celery worker с информацией на уровне журнала (-l info), вы можете прочитать подробное описание того, что происходит в конце Celery.

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

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

По истечении двадцати секунд Celery выводит в окно терминала фиктивное электронное письмо, которое Django создает с помощью send_mail(). Затем он добавляет еще одну запись в журнал, в которой сообщается, что send_feedback_email_task выполнено успешно, сколько времени это заняло (20.078754458052572s), и каково возвращаемое значение (None).

Примечание: Имейте в виду, что ваше приложение Django не будет знать, успешно ли выполнена задача Celery в этом примере. Это означает, что Спасибо! сообщение, которое видит ваш читатель, не обязательно означает, что оно дошло до вас в целости и сохранности. Поскольку вы настраиваете серверную часть базы данных с помощью Redis, вы можете запросить эту серверную часть, чтобы определить, был ли запуск задачи успешным или нет.

Из-за того, как работает HTTP, информирование пользователя на интерфейсе об успешном завершении фоновой задачи не является тривиальной задачей. Для достижения этой цели вам потребуется настроить AJAX-опрос или веб-сокетов через каналы Django.

Все прошло хорошо! Похоже, ваш отзыв был отправлен быстро, и вам не пришлось долго ждать, что может вас огорчить.

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

Заключение

Вжик! Отзыв отправлен!

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

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

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

В этом руководстве вы узнали, как:

  • Распознать эффективные варианты использования сельдерея
  • Различают Измельчителей сельдерея и Обработчиков сельдерея
  • Интеграция Celery и Redis в проект на Django
  • Настройка асинхронных задач, которые выполняются независимо от вашего приложения Django
  • Реорганизовать код Django, чтобы выполнить задачу с помощью Celery вместо

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

Знание того, что вы можете выполнять медленные задачи в фоновом режиме, не ухудшая работу пользователя, также открывает двери для новых идей:

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

Часто задаваемые вопросы

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

Эти часто задаваемые вопросы относятся к наиболее важным понятиям, которые вы рассмотрели в этом руководстве. Нажмите на переключатель Показывать/скрывать рядом с каждым вопросом, чтобы открыть ответ.

 

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

 

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

 

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

 

Чтобы настроить Celery в Django, вам нужно создать экземпляр приложения Celery, настроить посредника сообщений, такого как Redis, добавить настройки, относящиеся к Celery, в файл вашего проекта settings.py и определить свои задачи в tasks.py файл в вашем приложении Django.

 

Вы запускаете задачу в Celery, вызывая функцию task с помощью метода .delay(), который отправляет задачу рабочему Celery для асинхронного выполнения.

 

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

 

Celery workers - это процессы, которые выполняют задачи независимо от основного приложения, в то время как Celery beat - это планировщик, который управляет периодическим выполнением задач, указывая Celery workers, когда выполнять задачи.

 

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

<статус завершения article-slug="асинхронные задачи с django и сельдереем" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/articles/асинхронные задачи с django и сельдереем"/закладка/" data-api-article-completion-status-url="/api/v1/articles/asynchronous-tasks-with-django-and-celery/completion_status/"> <кнопка поделиться bluesky-text="Интересная статья на #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей о Python:%0A%0AAsynchronous Tasks With Django и Celery" email-subject="Статья о Python для вас" twitter-text="Интересная статья о Python от @realpython:" url="https://realpython.com/asynchronous-tasks-with-django-and-celery /" url-title="Асинхронные задачи с Django и Celery">

Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой Real Python. Посмотрите его вместе с письменным руководством, чтобы углубить свое понимание: Создание асинхронных задач с помощью Celery и Django

Вернуться на верх