Статьи

Использование сельдерея с Django для обработки фоновых задач

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

Когда это происходит, нужно проводить различие между тем, что должно происходить мгновенно (обычно в жизненном цикле HTTP-запроса), и тем, что может произойти в конце концов. Это почему? Ну, потому что, когда ваше приложение перегружено трафиком, простые вещи, подобные этому, имеют значение.

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

  • должно произойти мгновенно: операции времени запроса
  • должно произойти в конце концов: фоновые задачи

Операции времени запроса могут выполняться в одном цикле запрос / ответ, не беспокоясь о том, что время ожидания истечет или что у пользователя могут возникнуть проблемы. Типичные примеры включают операции с базой данных CRUD (создание, чтение, обновление, удаление) и управление пользователями (процедуры входа / выхода из системы).

Фоновые задачи различаются, так как они обычно занимают много времени и подвержены сбоям, в основном из-за внешних зависимостей. Некоторые общие сценарии среди сложных веб-приложений включают в себя:

  • отправка писем с подтверждением или активностью
  • ежедневно сканирует и копирует информацию из разных источников и хранит их
  • выполнение анализа данных
  • удаление ненужных ресурсов
  • экспорт документов / фотографий в различные форматы

Фоновые задачи являются основной целью этого урока. Наиболее распространенным шаблоном программирования, используемым для этого сценария, является Producer Consumer Architecture.

Проще говоря, эту архитектуру можно описать так:

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

Обычно потребители извлекают задачи из очереди по принципу «первым пришел — первым вышел» (FIFO) или в соответствии со своими приоритетами. Потребителей также называют рабочими, и этот термин мы будем использовать повсюду, поскольку он соответствует терминологии, используемой обсуждаемыми технологиями.

Какие задачи можно обрабатывать в фоновом режиме? Задачи, которые:

  • не важны для базовой функциональности веб-приложения
  • не может быть запущен в цикле запрос / ответ, так как они медленные (интенсивный ввод-вывод и т. д.)
  • зависит от внешних ресурсов, которые могут быть недоступны или работать не так, как ожидалось
  • возможно, потребуется повторить попытку хотя бы один раз
  • должны быть выполнены по графику

Сельдерей является де-факто выбором для выполнения фоновых задач в экосистеме Python / Django. Он имеет простой и понятный API и прекрасно интегрируется с Django. Он поддерживает различные технологии для очереди задач и различные парадигмы для рабочих.

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

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

1
$ pip install Django

Я решил создать еще одно приложение для блогов. В центре внимания приложения будет простота. Пользователь может просто создать учетную запись и без лишних хлопот может создать сообщение и опубликовать его на платформе.

Настройте quick_publisher Django quick_publisher :

1
$ django-admin startproject quick_publisher

Давайте запустим приложение:

1
2
$ cd quick_publisher
$ ./manage.py startapp main

При запуске нового проекта Django мне нравится создавать main приложение, которое содержит, помимо прочего, пользовательскую модель пользователя. Чаще всего я сталкиваюсь с ограничениями модели Django User по умолчанию. Наличие User модели User дает нам преимущество гибкости.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# main/models.py
 
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
 
 
class UserAccountManager(BaseUserManager):
    use_in_migrations = True
 
    def _create_user(self, email, password, **extra_fields):
        if not email:
            raise ValueError(‘Email address must be provided’)
 
        if not password:
            raise ValueError(‘Password must be provided’)
 
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user
 
    def create_user(self, email=None, password=None, **extra_fields):
        return self._create_user(email, password, **extra_fields)
 
    def create_superuser(self, email, password, **extra_fields):
        extra_fields[‘is_staff’] = True
        extra_fields[‘is_superuser’] = True
 
        return self._create_user(email, password, **extra_fields)
 
 
class User(AbstractBaseUser, PermissionsMixin):
 
    REQUIRED_FIELDS = []
    USERNAME_FIELD = ’email’
 
    objects = UserAccountManager()
 
    email = models.EmailField(’email’, unique=True, blank=False, null=False)
    full_name = models.CharField(‘full name’, blank=True, null=True, max_length=400)
    is_staff = models.BooleanField(‘staff status’, default=False)
    is_active = models.BooleanField(‘active’, default=True)
 
    def get_short_name(self):
        return self.email
 
    def get_full_name(self):
        return self.email
 
    def __unicode__(self):
        return self.email

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

Теперь нам нужно указать Django использовать эту модель пользователя вместо модели по умолчанию. Добавьте эту строку в файл quick_publisher/settings.py :

Нам также нужно добавить main приложение в список INSTALLED_APPS в quick_publisher/settings.py . Теперь мы можем создать миграции, применить их и создать суперпользователя, чтобы иметь возможность войти в админ-панель Django:

1
2
3
$ ./manage.py makemigrations main
$ ./manage.py migrate
$ ./manage.py createsuperuser

Давайте теперь создадим отдельное приложение Django, которое отвечает за сообщения:

1
$ ./manage.py startapp publish

Давайте определим простую модель Post в publisher/models.py :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
from django.db import models
from django.utils import timezone
from django.contrib.auth import get_user_model
 
 
class Post(models.Model):
    author = models.ForeignKey(get_user_model())
    created = models.DateTimeField(‘Created Date’, default=timezone.now)
    title = models.CharField(‘Title’, max_length=200)
    content = models.TextField(‘Content’)
    slug = models.SlugField(‘Slug’)
 
    def __str__(self):
        return ‘»%s» by %s’ % (self.title, self.author)

Подключение модели Post с помощью администратора Django выполняется в файле publisher/admin.py следующим образом:

1
2
3
4
5
6
7
from django.contrib import admin
from .models import Post
 
 
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    pass

Наконец, давайте подключим приложение publisher к нашему проекту, добавив его в список INSTALLED_APPS .

Теперь мы можем запустить сервер и перейти на http://localhost:8000/admin/ и создать наши первые посты, чтобы у нас было что поиграть:

1
$ ./manage.py runserver

Я надеюсь, что вы сделали свою домашнюю работу и создали сообщения.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
# publisher/views.py
 
from django.http import Http404
from django.shortcuts import render
from .models import Post
 
 
def view_post(request, slug):
    try:
        post = Post.objects.get(slug=slug)
    except Post.DoesNotExist:
        raise Http404(«Poll does not exist»)
 
    return render(request, ‘post.html’, context={‘post’: post})

Давайте quick_publisher/urls.py наш новый вид с URL в: quick_publisher/urls.py

01
02
03
04
05
06
07
08
09
10
11
# quick_publisher/urls.py
 
from django.conf.urls import url
from django.contrib import admin
 
from publisher.views import view_post
 
urlpatterns = [
    url(r’^admin/’, admin.site.urls),
    url(r’^(?P<slug>[a-zA-Z0-9\-]+)’, view_post, name=’view_post’)
]

Наконец, давайте создадим шаблон, который отображает сообщение в: publisher/templates/post.html

01
02
03
04
05
06
07
08
09
10
11
12
<!DOCTYPE html>
<html>
<head lang=»en»>
    <meta charset=»UTF-8″>
    <title></title>
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
    <p>Published by {{ post.author.full_name }} on {{ post.created }}</p>
</body>
</html>

Теперь мы можем перейти в http: // localhost: 8000 / the-slug-of-the-post-you-созданный / в браузере. Это не совсем чудо веб-дизайна, но создание красивых постов выходит за рамки этого урока.

Вот классический сценарий:

  • Вы создаете учетную запись на платформе.
  • Вы предоставляете адрес электронной почты для уникальной идентификации на платформе.
  • Платформа проверяет, действительно ли вы являетесь владельцем адреса электронной почты, отправив письмо со ссылкой для подтверждения.
  • Пока вы не выполните проверку, вы не сможете (полностью) использовать платформу.

Давайте добавим флаг is_verified и is_verified модели User :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# main/models.py
import uuid
 
 
class User(AbstractBaseUser, PermissionsMixin):
 
    REQUIRED_FIELDS = []
    USERNAME_FIELD = ’email’
 
    objects = UserAccountManager()
 
    email = models.EmailField(’email’, unique=True, blank=False, null=False)
    full_name = models.CharField(‘full name’, blank=True, null=True, max_length=400)
    is_staff = models.BooleanField(‘staff status’, default=False)
    is_active = models.BooleanField(‘active’, default=True)
    is_verified = models.BooleanField(‘verified’, default=False) # Add the `is_verified` flag
    verification_uuid = models.UUIDField(‘Unique Verification UUID’, default=uuid.uuid4)
 
    def get_short_name(self):
        return self.email
 
    def get_full_name(self):
        return self.email
 
    def __unicode__(self):
        return self.email

Давайте воспользуемся этим случаем, чтобы добавить модель User к администратору:

1
2
3
4
5
6
7
from django.contrib import admin
from .models import User
 
 
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    pass

Давайте внесем изменения в базу данных:

1
2
$ ./manage.py makemigrations
$ ./manage.py migrate

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

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

Мы собираемся создать обратный вызов, который будет запущен после создания пользовательской модели. Мы добавим этот код после определения модели User в: main/models.py

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
from django.db.models import signals
from django.core.mail import send_mail
 
 
def user_post_save(sender, instance, signal, *args, **kwargs):
    if not instance.is_verified:
        # Send verification email
        send_mail(
            ‘Verify your QuickPublisher account’,
            ‘Follow this link to verify your account: ‘
                ‘http://localhost:8000%s’ % reverse(‘verify’, kwargs={‘uuid’: str(instance.verification_uuid)}),
            ‘[email protected]’,
            [instance.email],
            fail_silently=False,
        )
 
signals.post_save.connect(user_post_save, sender=User)

Что мы сделали здесь, так это то, что мы определили функцию user_post_save и соединили ее с сигналом post_save (тот, который срабатывает после сохранения модели), отправляемым моделью User .

Джанго не просто отправляет электронные письма самостоятельно; это должно быть привязано к почтовому сервису. Для простоты вы можете добавить свои учетные данные Gmail в quick_publisher/settings.py или добавить своего любимого почтового провайдера.

Вот как выглядит конфигурация Gmail:

1
2
3
4
5
EMAIL_USE_TLS = True
EMAIL_HOST = ‘smtp.gmail.com’
EMAIL_HOST_USER = ‘<YOUR_GMAIL_USERNAME>@gmail.com’
EMAIL_HOST_PASSWORD = ‘<YOUR_GMAIL_PASSWORD>’
EMAIL_PORT = 587

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

Вот как проверить аккаунт:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
# main/views.py
 
from django.http import Http404
from django.shortcuts import render, redirect
from .models import User
 
 
def home(request):
    return render(request, ‘home.html’)
 
 
def verify(request, uuid):
    try:
        user = User.objects.get(verification_uuid=uuid, is_verified=False)
    except User.DoesNotExist:
        raise Http404(«User does not exist or is already verified»)
 
    user.is_verified = True
    user.save()
 
    return redirect(‘home’)

quick_publisher/urls.py представления в: quick_publisher/urls.py

01
02
03
04
05
06
07
08
09
10
11
12
13
14
# quick_publisher/urls.py
 
from django.conf.urls import url
from django.contrib import admin
 
from publisher.views import view_post
from main.views import home, verify
 
urlpatterns = [
    url(r’^$’, home, name=’home’),
    url(r’^admin/’, admin.site.urls),
    url(r’^verify/(?P<uuid>[a-z0-9\-]+)/’, verify, name=’verify’),
    url(r’^(?P<slug>[a-zA-Z0-9\-]+)’, view_post, name=’view_post’)
]

Также не забудьте создать файл home.html в main/templates/home.html . Это будет предоставлено home видом.

Попробуйте запустить весь сценарий снова и снова. Если все в порядке, вы получите электронное письмо с действительным проверочным URL. Если вы перейдете по URL-адресу, а затем зарегистрируетесь у администратора, вы увидите, как была проверена учетная запись.

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

Вот как это работает: мы отправляем пользовательские данные в приложение Django. Приложение создает модель User а затем создает соединение с Gmail (или другой выбранной вами службой). Django ждет ответа и только потом возвращает ответ нашему браузеру.

Вот где приходит Celery. Сначала убедитесь, что он установлен:

1
$ pip install Celery

Теперь нам нужно создать приложение Celery в нашем приложении Django:

01
02
03
04
05
06
07
08
09
10
11
12
# quick_publisher/celery.py
 
import os
from celery import Celery
 
os.environ.setdefault(‘DJANGO_SETTINGS_MODULE’, ‘quick_publisher.settings’)
 
app = Celery(‘quick_publisher’)
app.config_from_object(‘django.conf:settings’)
 
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()

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

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

Вы можете установить Redis, следуя инструкциям на странице быстрого запуска Redis. Вам нужно будет установить библиотеку Redis Python, pip install redis и пакет, необходимый для использования Redis и Celery: pip install celery[redis] .

Запустите сервер Redis в отдельной консоли, например: $ redis-server

Давайте добавим конфиги, связанные с quick_publisher/settings.py Redis, в quick_publisher/settings.py :

1
2
3
4
5
6
# REDIS related settings
REDIS_HOST = ‘localhost’
REDIS_PORT = ‘6379’
BROKER_URL = ‘redis://’ + REDIS_HOST + ‘:’ + REDIS_PORT + ‘/0’
BROKER_TRANSPORT_OPTIONS = {‘visibility_timeout’: 3600}
CELERY_RESULT_BACKEND = ‘redis://’ + REDIS_HOST + ‘:’ + REDIS_PORT + ‘/0’

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

Вот как это сделать:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# main/tasks.py
 
import logging
 
from django.urls import reverse
from django.core.mail import send_mail
from django.contrib.auth import get_user_model
from quick_publisher.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 QuickPublisher account’,
            ‘Follow this link to verify your account: ‘
                ‘http://localhost:8000%s’ % reverse(‘verify’, kwargs={‘uuid’: str(user.verification_uuid)}),
            ‘[email protected]’,
            [user.email],
            fail_silently=False,
        )
    except UserModel.DoesNotExist:
        logging.warning(«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 , код сигнала превращается в:

01
02
03
04
05
06
07
08
09
10
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, но ожидали бы завершения задачи, а это не то, что нам нужно.

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

1
$ celery worker -A quick_publisher —loglevel=debug —concurrency=4

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

1
2
[2017-04-28 15:00:09,190: DEBUG/MainProcess] Task accepted: main.tasks.send_verification_email[f1f41e1f-ca39-43d2-a37d-9de085dc99de] pid:62065
[2017-04-28 15:00:11,740: INFO/PoolWorker-2] Task main.tasks.send_verification_email[f1f41e1f-ca39-43d2-a37d-9de085dc99de] succeeded in 2.5500912349671125s: None

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

  • ежемесячные отчеты
  • уведомления об активности (лайки, запросы дружбы и т. д.)
  • напоминания для выполнения определенных действий («Не забудьте активировать свой аккаунт»)

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

Давайте изменим модель Post чтобы мы могли учесть сценарий количества просмотров.

01
02
03
04
05
06
07
08
09
10
class Post(models.Model):
   author = models.ForeignKey(User)
   created = models.DateTimeField(‘Created Date’, default=timezone.now)
   title = models.CharField(‘Title’, max_length=200)
   content = models.TextField(‘Content’)
   slug = models.SlugField(‘Slug’)
   view_count = models.IntegerField(«View Count», default=0)
 
   def __str__(self):
       return ‘»%s» by %s’ % (self.title, self.author)

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

1
2
3
$ ./manage.py makemigrations
 
$ ./manage.py migrate

Давайте также view_post представление view_post Django для подсчета просмотров:

01
02
03
04
05
06
07
08
09
10
def view_post(request, slug):
   try:
       post = Post.objects.get(slug=slug)
   except Post.DoesNotExist:
       raise Http404(«Poll does not exist»)
    
   post.view_count += 1
   post.save()
 
   return render(request, ‘post.html’, context={‘post’: post})

Было бы полезно отобразить view_count в шаблоне. Добавьте этот <p>Viewed {{ post.view_count }} times</p> где-нибудь внутри файла publisher/templates/post.html . Сделайте несколько просмотров поста и посмотрите, как увеличивается счетчик.

Давайте создадим задачу Celery. Поскольку речь идет о постах, я собираюсь разместить их в publisher/tasks.py :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from django.template import Template, Context
from django.core.mail import send_mail
from django.contrib.auth import get_user_model
from quick_publisher.celery import app
from publisher.models import Post
 
 
REPORT_TEMPLATE = «»»
Here’s how you did till now:
 
{% for post in posts %}
        «{{ post.title }}»: viewed {{ post.view_count }} times |
 
{% endfor %}
«»»
 
 
@app.task
def send_view_count_report():
    for user in get_user_model().objects.all():
        posts = Post.objects.filter(author=user)
        if not posts:
            continue
 
        template = Template(REPORT_TEMPLATE)
 
        send_mail(
            ‘Your QuickPublisher Activity’,
            template.render(context=Context({‘posts’: posts})),
            ‘[email protected]’,
            [user.email],
            fail_silently=False,
        )

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

1
2
3
4
5
$ ./manage.py shell
 
In [1]: from publisher.tasks import send_view_count_report
 
In [2]: send_view_count_report.delay()

Надеюсь, вы получили отличный маленький отчет в своем электронном письме.

Давайте теперь создадим периодическое задание. Откройте quick_publisher/celery.py и зарегистрируйте периодические задачи:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
# quick_publisher/celery.py
 
import os
from celery import Celery
from celery.schedules import crontab
 
os.environ.setdefault(‘DJANGO_SETTINGS_MODULE’, ‘quick_publisher.settings’)
 
app = Celery(‘quick_publisher’)
app.config_from_object(‘django.conf:settings’)
 
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()
 
app.conf.beat_schedule = {
    ‘send-report-every-single-minute’: {
        ‘task’: ‘publisher.tasks.send_view_count_report’,
        ‘schedule’: crontab(), # change to `crontab(minute=0, hour=0)` if you want it to run daily at midnight
    },
}

Пока что мы создали расписание, которое будет запускать задачу publisher.tasks.send_view_count_report каждую минуту, как указано в записи crontab() . Вы также можете указать различные графики сельдерея Crontab .

Откройте другую консоль, активируйте соответствующую среду и запустите службу Celery Beat.

1
$ celery -A quick_publisher beat

Работа сервиса Beat заключается в том, чтобы продвигать задания в сельдерее в соответствии с графиком. Примите во внимание, что расписание заставляет задачу send_view_count_report запускаться каждую минуту в соответствии с настройкой. Это хорошо для тестирования, но не рекомендуется для реального веб-приложения.

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

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

Вот несколько выводов, которые мы можем сделать:

  • Рекомендуется оставлять ненадежные и трудоемкие задачи вне времени запроса.
  • Долгосрочные задачи должны выполняться в фоновом режиме рабочими процессами (или другими парадигмами).
  • Фоновые задачи могут использоваться для различных задач, которые не являются критическими для основного функционирования приложения.
  • Сельдерей может также справляться с периодическими задачами, используя сервис celery beat .
  • Задачи могут быть более надежными, если их сделать идемпотентными и повторить (возможно, с использованием экспоненциального отката).