DevGang
Авторизоваться

Асинхронные задачи в Django без Celery

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

Что такое асинхронные задачи Django?

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

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

Если вы искали в Google по щапросу «асинхронных задач Django», вероятно, вам будут попадаться преложения по работе с Celery как способом реализации асинхронных задач с помощью Django. Пожалуйста, не поймите меня неправильно: в Celery нет ничего плохого. Многие успешные проекты с используют Celery. Я также использовал Celery в нескольких проектах, и это хорошо работает. Что меня раздражает, так это дополнительные усилия.

Celery - еще один сервис для настройки, запуска и обслуживания.

Введение в uWSGI

Как я писал в своем другом посте, Django - NGINX: разверните свой проект Django на производственном сервере, мне нравится использовать сервер приложений uWSGI. Что ж, получается, что uWSGI предоставляет полнофункциональную, готовую к работе систему для реализации асинхронных задач. Они называют это Spooler uWSGI. Ссылаясь на документацию uWSGI:

Spooler - это менеджер очередей, встроенный в uWSGI, который работает как система печати / почты.

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

Spooler работает, определяя каталог, в который будут записываться «файлы спула», каждый раз, когда спулер находит файл в своем каталоге, он анализирует его и запускает определенную функцию.

Spooler uWSGI очень прост в настройке!

Установка пакета uwsgidecorators

Для взаимодействия с uWSGI Spooler из кода Python действительно удобно установить пакет uwsgidecorators. К сожалению, в PyPI нет последней версии uwsgidecorators, но вы можете установить ее с помощью apt:

sudo apt install python3-uwsgidecorators

Если вы используете virtualenv для вашего проекта Django (как вы должны), вы можете связать модуль uwsgidecorator с вашим virtualenv:

ln -s /usr/lib/python3/dist-packages/uwsgidecorators.py /path/to/your/virtualenv/lib/python3.6/site-packages

Создаем каталог для «файлов спула»

Прежде всего, вы должны создать каталог, в котором uWSGI будет сохранять «файлы спула», используемые Spooler:

mkdir -p /home/ubuntu/tasks
sudo chown www-data.www-data /home/ubuntu/tasks

Пожалуйста, убедитесь, что каталог доступен для записи пользователю uWSGI, то есть www-data в примере.

Создайте модуль задач в вашем приложении Django

Предполагая, что у вас есть приложение Django с именем app1, вы должны создать модуль с именем tasks.py в каталоге приложения. Содержимое модуля должно быть примерно таким:

import logging

try:
   from uwsgidecorators import spool
except:
   def spool(func):
       def func_wrapper(**arguments):
           return func(arguments)
       return func_wrapper

import django
django.setup()

from .models import Model1

logger = logging.getLogger(__name__)

@spool
def long_running_task(arguments):
   id = arguments['id']
   obj1 = Model1.objects.get(pk=id)
   obj1.long_running_model_method()

Конструкция try / catch позволит вам протестировать код, даже если он не запущен на сервере приложений uWSGI, например, при локальном запуске с использованием ./manage.py runserver. В этом случае задача будет выполняться синхронно, так как это был обычный вызов функции python.

Функция long_running_task - это задача, которую вы будете вызывать с помощью спулера uWSGI. Как вы видите, вы должны украсить его с помощью декоратора @spool, а параметры для задачи передаются в словаре с именем arguments.

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

Вызовите асинхронную задачу из вашего представления Django

Здесь вы можете найти, как вызвать асинхронную задачу из примера:

from django.shortcuts import redirect
from django.contrib import messages

from .tasks import long_running_task

def async_view(request, id):
   long_running_task(id=id)
   messages.add_message(request, messages.SUCCESS,
       'Task started correctly')
   return redirect('some_other_view')

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

Настройте uWSGI для использования спулера

Чтобы настроить спулер uWSGI, вы должны отредактировать файл конфигурации uWSGI, который я представил в посте Django - NGINX: разверните ваш проект Django на рабочем сервере. Файл находится здесь: /etc/uwsgi/apps-enabled/django.ini. Содержимое файла должно быть примерно таким:

[uwsgi]
chdir = /home/ubuntu/django_project # customize with your django installation directory
env = DJANGO_SETTINGS_MODULE=project.settings.production # customize with your settings module
wsgi-file = project/wsgi.py # customize with the relative path to your wsgi.py file
workers = 1
spooler-chdir = /home/ubuntu/django_project # customize with your django installation directory
spooler = /home/ubuntu/tasks/
import = app1.tasks

Перезапустите uWSGI с помощью:

service uwsgi restart

Вы найдете журналы uWSGI в /var/log/uwsgi/apps/django.log . Поэтому вы можете проверить их, чтобы увидеть, правильно ли запущен процесс Python или есть проблемы.

В файле журнала вы также найдете сообщения о Spooler, что-то вроде этого:

Mon Nov 18 14:50:45 2019 - [spooler /home/ubuntu/tasks pid: 8646] managing request 
uwsgi_spoolfile_on_www.example.com_8647_3_1383635828_1574067630_419736 ...

Вывод

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

Перевод статьи: Django asynchronous tasks without Celery

#Python #Django #uWSGI
Комментарии 2
Psiho Nort 24.04.2020 в 13:09

Это же перевод статьи, https://www.guguweb.com/2019/11/21/django-asynchronous-tasks-without-celery/ . Источник бы указали, а то некрасиво получается.

LegGnom 25.04.2020 в 20:37

Спасибо за замечание, добавили источник

Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

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

Попробовать

Напиши статью и выиграй годовую подписку на Яндекс плюс или лицензию от Jet Brains

Участвовать