Мы часто сталкиваемся с асинхронными задачами в веб-разработке. Когда нам нужны такие операции как:
Отправка электронных писем
Отправка запросов к внешним API
Долгие математические операции
Сложные запросы к базе данных
Наше приложение тратит время на их выполнение и заставляет пользователя ждать, что может негативно сказаться на его пользовательском опыте. Для решения этой проблемы существуют асинхронные очереди выполнения задач.
Celery и его проблемы
Самой популярной для языка программирования Python давно является Celery. Она предоставляет большие возможности работы с задачами, запуске их с определенным периодом и тонкой настройки.
До выхода Python 3.7 она была чуть ли не единственным способом реализовать асинхронную обработку задач. Но она, к сожалению, также известна своими проблемами с совместимостью с Python 3.7 и выше и отсутствием поддержки Windows с версии Celery 4.
И поэтому для тестирования своего приложения приходится разворачивать Сelery в Docker или переходить на Linux, что для многих является нежелательным.
Современное решение - Dramatiq
Поискав информацию я обнаружил прекрасную альтернативу Celery.
Dramatiq - это простая в использовании и надежная библиотека распределенной обработки задач для Python 3, написанная в 2017 году для решения проблем Celery.
If you’ve ever had to use Celery in anger, Dramatiq could be the tool for you.
Если вам когда-либо приходилось использовать Celery, испытывая гнев, то Dramatiq может быть инструментом для вас.
Так описывает Dramatiq автор библиотеки Богдан Поп. Какие же плюсы есть у Dramatiq?
Активно разрабатывается и используется в производстве
Отличная документация
Автоматическая перезагрузка кода ваших задач
Поддержка Redis и RabbitMQ
Понятный исходный код, который позволяет сообществу развивать библиотеку
И если вы ищете простую очередь выполнения задач, Dramatiq вам точно понравится. Ниже я приведу сравнительную таблицу актуальных решений в этой сфере:
| Dramatiq | Celery | RQ |
Поддержка Python 2 | Нет | Да | Да |
Поддержка Windows | Да | Нет | Нет |
Простой исходный код | Да | Нет | Да |
Отложенные задачи | Да | Да | Нет |
Планирование задач | Нет | Да | Нет |
Перезагрузка кода | Да | Нет | Нет |
Поддержка RabitMQ | Да | Да | Нет |
Поддержка Redis | Да | Да | Да |
Пример асинхронной задачи с помощью Dramatiq
Хватит разговоров, давайте посмотрим на Dramatiq в действии!
Напишем простую функцию, получающую заголовки веб-страниц, и записывающую их в файл titles.txt. Для этого создадим файл titles_scraper.py и напишем в нем следующий код:
import requests
from bs4 import BeautifulSoup
def get_page_title(url):
soup = BeautifulSoup(requests.get(url).text, "html.parser")
file = open("titles.txt", "a")
file.write("\n"+soup.title.text)
Пока что наша функция может быть вызвана только как get_page_title("example.com")
и будет заставлять нас ждать, не давая выполнить более полезную работу, чем ожидание ответа с сервера. Давайте преобразуем нашу функцию и сделаем так, чтобы можно было запускать ее в отдельной очереди Dramatiq.
Для этого нам достаточно лишь добавить нужный декоратор нашей функции и выбрать, какой брокер мы будем использовать:
import requests
from bs4 import BeautifulSoup
from dramatiq.brokers.redis import RedisBroker
import dramatiq
# Я буду использовать Redis как брокера
dramatiq.set_broker(RedisBroker())
@dramatiq.actor
def get_page_title(url):
soup = BeautifulSoup(requests.get(url).text, "html.parser")
file = open("titles.txt", "a")
file.write("\n"+soup.title.text)
И это все, что надо сделать, чтобы объявить функцию Dramatiq. Вам не надо настраивать и долго конфигурировать Dramatiq для первого запуска, но при этом остается возможность настройки, когда это нужно.
Теперь запустим очередь задач:
dramatiq titles_scraper
Откроем консоль Python и попробуем выполнить нашу функцию:
>>> from titles_scraper import get_page_title
>>> get_page_title("https://google.com/") # Вам придется ждать около секунды
>>> get_page_title.send("https://google.com") # Код выполнится мгновенно,
>>> # Dramatiq сам занялся выполнением кода
Теперь, открыв файл titles.txt, вы увидите заголовок страницы google.com.
Естественно, данный пример показывает лишь малую часть возможностей Dramatiq, которые помогут вам с асинхронными очередями задач. Цель данного примера была лишь в том, чтобы показать простоту Dramatiq.
Поддержка современными веб-фреймворками
Существуют также специализированные библиотеки для работы с Django и Flask:
Если вы используете Django, вы можете задействовать django-dramatiq. Создан специальный демонстрационный проект с использованием данного пакета.
Если вы приверженец Flask, вы сможете попробовать flask-dramatiq.
Но если вы используете другой фреймворк, вам ничего не мешает разрабатывать с помощью чистого Dramatiq или даже написать свою библиотеку для работы с ним в вашем фреймворке.