Задачи (Tasks)
Задачи иногда также называют сообщениями. По сути брокер сообщений - это нечто, что передает сообщения из одной системы в другую. В нашем случае сообщение представляет собой описание задачи: название (уникальный идентификатор), входные параметры, время ожидания, количество повторных попыток и тд.
В celery задача является классом. Таким образом, каждый раз, когда вы используете декоратор для функции (например, @shared_task), чтобы сделать ее celery задачей, под капотом создается класс. Это означает, что у каждой задачи есть self, к которому добавляется множество атрибутов, например: name, request, status, priority, retries и многое другое. Если мы хотим получить доступ к этим атрибутам, то нужно указать параметр bind=True.
Прежде чем что-либо может быть запущено в Celery, оно должно быть декларировано как задача.
Вот как это сделать, добавим в main/tasks.py следующий код:
from django.urls import reverse
from django.core.mail import send_mail
from django.contrib.auth import get_user_model
from publish.celery import app
@app.task
def send_verification_email(user_id):
UserModel = get_user_model()
try:
user = UserModel.objects.get(pk=user_id)
send_mail(
'Verify your account',
'Follow this link to verify your account: '
'http://localhost:8000%s' % reverse('verify', kwargs={'uuid': str(user.verification_uuid)}),
'admin@localhost.ru',
[user.email],
fail_silently=False,
)
except UserModel.DoesNotExist:
print("Tried to send verification email to non-existing user '%s'" % user_id)
Мы здесь сделали следующее: мы переместили функцию отправки по почты в другой файл под названием tasks.py.
Несколько примечаний:
- Имя файла имеет значение. Celery проходит через все приложения в
INSTALLED_APPSи регистрирует задачи в файлахtasks.py. - Обратите внимание, как мы декорировали
send_verification_emailс помощью@app.task. Это указывает Celery, что это задача, которая будет выполняться в очереди задач. - Обратите внимание, что мы ожидаем аргумент
user_id, а не объектUser. Это связано с тем, что при отправке задач на Celery может возникнуть проблема с сериализацией сложных объектов. Лучше использовать примитивные типы.
Возвращаясь к main/models.py, код сигнала преобразуется в:
from django.db.models import signals
from main.tasks import send_verification_email
def user_post_save(sender, instance, signal, *args, **kwargs):
if not instance.is_verified:
# Send verification email
send_verification_email.delay(instance.pk)
signals.post_save.connect(user_post_save, sender=User)
Обратите внимание, как мы вызываем метод .delay объекта задачи. Это означает, что мы отправляем задание на Celery, и мы не ожидаем результата. Если бы мы использовали send_verification_email (instance.pk), мы все равно отправили бы его в Celery и ждали завершения задачи, чего мы не хотим.
Прежде чем Вы начнете создавать нового пользователя, есть есть загвоздка. Celery - это сервис, и нам нужно его запустить. Откройте новую консоль и введите. На данном этапе мы используем 2 терминала, первый это запущенный Redis, второй это запущенный Celery.
celery -A publish worker --loglevel=info
Эта команда запустит процесс воркеров Celery.
Для пользователей Windows сначала необходимо установить библиотеку eventlet:
pip install eventlet
А для запуска необходимо использовать такую команду:
celery -A publish worker -P eventlet --loglevel=info
Мы также видим что в списке задач у нас появилась задача main.tasks.send_verification_email:
Осталось запустить сервер еще в одном терминале и теперь Вы можете, наконец, перейти к созданию пользователя.
После создания, обратите внимание на то, что нет задержки при создании, и также мы можем следить за логами в консоли Celery и посмотреть, правильно ли выполняются задачи. Это должно выглядеть примерно так:
Осталось проверить почту:
Как мы видим, отправка писем через Celery прекрасно работает и мы избавились от задержки отправки письма при создании пользователя.