Асинхронные задания в Django

Асинхронные задания в Django

Кирилл Перевозчиков @ DataRobot

github.com/Axik

О чём будет идти речь?

Нужно ли использовать очереди задач?

Django блокирующийся фреймворк

Нужно, если в цикле request-response существует операция существенная повышающая время задержки ответа.

Типичные примеры

  • Рассылка электронных писем
  • Вытягивание большого количества данных с third-party web-сервиса
  • Insert или Update большого количества объектов в БД
  • Выполнение времязатратных вычислений
  • Отправка или обработка web-hooks

Окей, Google

"Django task queue project"

Celery

Pros:
  • Стандарт для Django
  • Можно использовать разные исполнения брокеров
  • Гибкий в настройке
  • Отлично подходит для больших проектов

Celery

Cons:
  • Гибкий в настройке
  • Тяжеловесный: множество процессов + external dependencies
  • Неподходит для маленьких приложений
  • Исходный код нечитабелен. 27k lines

Celery

Celery

Redis Queue

Pros:
  • Гибкий в настройке
  • Менее требовательный к памяти, чем Celery
  • Отлично подходит для средних и маленьких проектов
  • 3k lines. Прост и понятен.

Redis Queue

Cons:
  • Только redis как брокер.
  • Менее гибкая система настройки очереди, чем Celery
  • Воркеры могут быть только Python.
  • Простые плюшки задач по расписанию или Django-админки отсутствуют из коробки.

Django-background-tasks

Pros:
  • Очень прост в настройке
  • Очень прост в использовании
  • Не требует дополнительных зависимостей, использует Django ORM как брокер.
  • Отлично подходит для прототипов, либо маленьких проектов

Django-background-tasks

Cons:
  • Django ORM как брокер

Какой же выбрать?

Сложная специальная логика для очереди, распределённая система воркеров?

Celery

Какой же выбрать?

Отсутствует специальная логика для очереди, не нужна распределённая система воркеров и хочется понимать исходный код библиотеки?

Redis Queue + Django RQ + RQ-Scheduler

Какой же выбрать?

Прототип на вчера, и не охота возиться с Redis?

Django-background-task

Celery case. RabbitMQ vs. Redis.

RabbitMQ

  • Гибко настраиваемая маршрутизация
  • Persistent queues (Гарантированная доставка)

Celery case. RabbitMQ vs. Redis.

Redis

  • Высокая скорость обработки сообщений по сравнению с RabbitMQ
  • Можно использовать одновременно и как key-value storage и как брокер в одной системе

Celery case. RabbitMQ vs. Redis.

Winner?

Redis, если:

Celery case. RabbitMQ vs. Redis.

Winner?

RabbitMQ, если:

Redis не быстрее RabbitMQ

tasks.py

		from celery import Celery
		celery = Celery('tasks', broker='amqp://guest@localhost//') 
		#celery = Celery('tasks', broker='redis://localhost//') 
		
			
		
		@celery.task
		def newtask(somestr, dt, value):
		    pass
		

test.py

		from tasks import newtask
		from datetime import datetime
		import time
		
		
		dt = datetime.utcnow()
		st_time = time.time()
		for i in xrange(100000):
		    newtask.delay('shortstring', dt, 67.8)
		print(time.time() - st_time)
		

Запускаем celery worker

			time celery -A tasks worker --loglevel=info  -f tasks.log --concurrency 1
		

Результаты

Операция Попытка 1 Попытка 2 Попытка 3 Среднее Цена одного сообщения
RabbitMQ - Добавление задачи в очередь 56.96 54.18 57.13 56.09 0.0005609
Redis - Добавление задачи в очередь 68.81 76.52 76.95 74.09 0.0007409
RabbitMQ - Обработка сообщения из очереди 122.406 132.55 195.885 150.28 0.0015028
Redis - Обработка сообщения из очереди 157.59 177.774 186.332 173.9 0.001739

Время добавление в очередь RabbitMQ составляет 75% от времени Redis.

Время обработки сообщения - 86%.

Pro tips & tricks.

Не используйте базу данных как брокер.

Используйте больше, чем одну очередь для разных задач

Храните результаты выполнения, только если они вам нужны

Не передавать Django-ORM объекты в задачу.

Старайтесь передавать json-serializeble данные в функцию-задачи.

Применяйте философию views.py для tasks.py

Отложенные задачи, не значит бесплатные

Monitoring

Периодические очищайте "умершие" задачи

Используйте отдельную очередь для повтора задач с ошибкой

Использованные ресурсы

Источник лекции на гитхабе