Django, Celery и Redis
Эта небольшая статья предназначена для того, кто еще не знаком с Celery и не знает что это такое. В статье рассказывает как быстро запустить Celery вместе с Django, для выполнения асинхронных задач которые будут выполнятся отдельно от процесса Django. Классическим примером такой задачи может быть отправка email (мы ее рассмотрим ниже). Хотя это может быть любая задача вся обработка которой должна происходит отдельно от запроса пользователя. Еще один распространенным примером может быть загрузка и последующая конвертация медиа файлов.
Базовая документация по настройке Celery с Django находится тут
Итак для начало создадим новую директорию с каким-нибудь названием типа temp:
mkdir temp cd temp
Далее создадим виртуальное окружение с помощью pipenv c python 3.6 При установке python 3.7 возможно появление проблемы запуска Celery beat описанная тут
pipenv --python 3.6 pipenv shell
Далее установим Django и Celery. В качестве базы данных для Celery мы будем использовать Redis, поэтому так же установим поддержку Redis для Celery
pipenv install django pipenv install celery pipenv install celery[redis]
Теперь создадим наш проект на Django и сразу запустим миграцию базы по умолчанию.
django-admin startproject testapp ... cd testapp python manage.py migrate
Создадим новое приложение в Django и папку для шаблонов.
django-admin startapp home mkdir templates
После его создания подключим его в INSTALLED_APPS, настроим папку шаблонов
testapp/settings.py
... INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'home' ] ... TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], .... }, ] ... BROKER_URL = 'redis://127.0.0.1:6379/0' BROKER_TRANSPORT = 'redis' ...
Далее создадим файл index.html с дефолной версткой
templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <h1>Hello world</h1> </body> </html>
Создадим новый файл Celery testapp/celery.py
import os import django from celery import Celery from django.conf import settings os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testapp.settings') django.setup() app = Celery('testapp') app.config_from_object('django.conf:settings') app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
Создадим файл задач testapp/tasks.py
from time import sleep from testapp.celery import app @app.task def hello_world(): sleep(10) # поставим тут задержку в 10 сек для демонстрации ассинхрности print('Hello World')
Создадим вьюху по умолчанию для обработки запроса home/views.py
from django.shortcuts import render from django.views.generic import TemplateView from testapp.tasks import hello_world class IndexView(TemplateView): template_name = 'index.html' def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) hello_world() return context
Сейчас само время проверить как это работает. Запустим сервер Django
python manage.py runserver
И зайдем в браузере по адресу http://127.0.0.1:8000 то через 10 секунд мы должны увидеть ответ от сервера.
В консоли мы должны увидеть что то типа следующего
Обратите внимание на Hello world. Наша тестовая задача была выполнена после 10 сек от получения запроса.
Теперь попробуем запустить нашу задачу через Celery. Для этого отредактируйте файл home/views.py что бы додавить в него hello_world.delay()
def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) hello_world.delay() return context
Убедитесь что у вас запущен сервер Redis
redis-server
Так же теперь нам нужно запустит Celery. Для этого в новом окне терминала войдем в наше виртуальное окружение и выполним команду запуска Celery
pipenv shell celery worker -A testapp -l debug
Теперь можно снова зайти на http://127.0.0.1:8000 и мы сразу получим ответ от сервера, а через 10 секунд можно будет увидеть в консоле выполнение нашей задачи.
Асинхронная отправка Email
Внесем настройки email в Django в файл testpp/settings.py
... EMAIL_HOST = 'XXXX' EMAIL_PORT = '465' EMAIL_HOST_USER = 'XXXX' EMAIL_HOST_PASSWORD = 'XXXX' EMAIL_USE_TLS = False EMAIL_USE_SSL = True ...
Далее создадим задачу для отправки email. Для этого в файл testapp/tasks.py внесем следующее:
from time import sleep from django.core.mail import send_mail @app.task def send_email_task(): # сделаем задержку на 10 сек для демонстрации ассинхроности sleep(10) send_mail('Тема письма', 'Тело письма', 'адрес_отправителя@mail.com', ['адрес_получателя@mail.com'])
Далее вызовем эту задачу в нашей вьюхе home/views.py:
... def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) send_email_task.delay() return context
Все этого достаточно. Теперь при обращение к http://127.0.0.1:8000 от сервера будет сразу получен ответ, а через 10 сек по указанному адресу будет отправлено письмо.
Celery beat
Работа Celery в режиме beat заключается в том, чтобы запускать задачи Celery в соответствии с расписанием. Допустим у нас есть задача которую необходимо запускать по расписанию, например нам нужно чистить какую нибудь папку каждые 5 минут.
Внесем наше расписание в testapp/celery.py Время запуска устанавливается через crontab.
... from celery.schedules import crontab ... app.conf.beat_schedule = { 'run-every-single-minute': { 'task': 'testapp.tasks.hello_world', 'schedule': crontab(), }, }
Далее нам потребуется запустить в еще одном терминальном окне Celery в режиме beat
celery -A testapp beat -l debug
Заключение
Надеюсь это небольшая статья помогла вам быстро разобраться с настройка Celery для Django. Если у вас остались вопросы пишите их в комментариях.
Добрый день ! Как запустить celery на web хостинге от reg.ru??