Ось урок, створений у стилі CS50, спеціально для тебе.
🎓 CS50: RabbitMQ та Асинхронність у Django
Привіт, друзі! Радий вас бачити. 👋
Сьогодні ми не просто пишемо код. Ми вчимося керувати часом і чергами. Ми зробимо так, щоб ваш Django-проєкт літав, навіть коли йому потрібно перелопатити гігабайти даних.
1. 🔥 Вступ: Чому ваш сайт "гальмує"?
Уявіть ситуацію. Ви заходите в кав'ярню вранці. Ви замовляєте складне лате з трьома сиропами. Касир приймає замовлення і... завмирає. Він не бере гроші у наступного клієнта. Він не відповідає на питання. Він стоїть і дивиться на кавомашину, поки ваше лате не буде готове. 5 хвилин. Черга за вами починає нервувати. Хтось іде геть.
Звучить жахливо, правда?
Але саме так працює стандартний HTTP-запит у Django! 1. Користувач натискає кнопку "Зареєструватися". 2. Django зберігає юзера в базу. 3. Django починає відправляти вітальний email (це довго!). 4. Браузер користувача "крутиться" і чекає... чекає...
Питання до вас: Чи повинен користувач чекати 3 секунди на завантаження сторінки тільки тому, що ваш SMTP-сервер повільно відправляє листи?
Звісно, ні.
Нам потрібен бармен (RabbitMQ). У правильній кав'ярні касир (Django) бере замовлення, пише ваше ім'я на стаканчику і ставить його в чергу. І одразу приймає наступного клієнта. А десь там, на фоні, бариста (Worker) бачить стаканчик і починає готувати.
Сьогодні ми навчимо Django делегувати важку роботу.
2. 🧠 Що під капотом? (Без нудної теорії)
Давайте розберемо це на простій схемі. У нас є три головні дійові особи.
1. Producer (Виробник) — це ваш Django
Його робота — швидко прийняти запит від користувача і сказати: "Окей, я це зроблю... пізніше". Він створює задачу (Task).
2. Broker (Брокер) — це RabbitMQ 🐰
Це наша поштова скринька або черга стаканчиків у кав'ярні. RabbitMQ — це дуже швидка і надійна програма, яка просто приймає повідомлення і тримає їх у собі, поки хтось їх не забере.
Інтуїтивно: Це просто буфер. Склад. Список справ.
3. Consumer (Споживач) — це Celery Worker
Це окремий процес (не той, що показує сайт), який постійно "стукає" до RabbitMQ: "Є робота? Є робота?". Як тільки з'являється задача, він її хапає і виконує.
🔑 Що треба запам'ятати:
- Django (Web) і Worker (Фонові задачі) — це різні процеси. Якщо Worker впаде, сайт продовжить працювати (просто листи не відправляться).
- RabbitMQ — це клей між ними.
3. 🧪 Приклади: Від простого до реального
Для роботи нам знадобиться бібліотека Celery (Python-клієнт) і запущений сервер RabbitMQ.
Приклад №1: "Hello World" із затримкою
Уявіть, що у нас є функція, яка дуже довго думає.
# tasks.py
import time
from celery import shared_task
@shared_task
def heavy_calculation(x, y):
time.sleep(10) # Імітуємо важку роботу (10 секунд!)
return x + y
Питання: Що буде, якщо я викличу це у Django view ось так:
result = heavy_calculation(2, 2)?
Правильно, сайт "повисне" на 10 секунд. Користувач закриє вкладку.
Як робимо ми:
# views.py
from .tasks import heavy_calculation
def my_view(request):
# Ми використовуємо .delay()!
heavy_calculation.delay(2, 2)
return HttpResponse("Ми рахуємо! Результат буде пізніше.")
Що сталося? Метод .delay() миттєво відправив повідомлення в RabbitMQ і повернув управління. Користувач бачить відповідь за мілісекунди. А десь у терміналі Celery через 10 секунд з'явиться результат 4.
Приклад №2: Реальність — Відправка Email
Ось типова помилка новачків. Передача об'єктів.
Як НЕ треба робити:
# Поганий код!
@shared_task
def send_email_task(user_object):
# Чому погано?
# Поки задача дійде до черги, user_object може змінитися в базі!
# Або pickle-серіалізація може зламатися.
user_object.email_user("Привіт!")
Як мислить профі: Ми передаємо тільки ID (первинний ключ). Це легкі дані, і вони завжди актуальні.
# tasks.py
from django.contrib.auth.models import User
from celery import shared_task
@shared_task
def send_welcome_email_task(user_id):
try:
user = User.objects.get(pk=user_id) # Дістаємо свіжого юзера з БД
user.email_user("Привіт!", "Ласкаво просимо на наш курс!")
except User.DoesNotExist:
print(f"Юзера з id {user_id} вже не існує!")
# views.py
def register(request):
# ... логіка створення юзера ...
user.save()
# Віддаємо RabbitMQ тільки ID
send_welcome_email_task.delay(user.id)
return redirect('home')
4. 🛠 Практична частина
Час забруднити руки! Запустіть RabbitMQ (через Docker або локально) і виконайте наступне:
🔹 Завдання 1: Ping-Pong
Налаштуйте мінімальний Django+Celery проєкт. Створіть таску, яка просто друкує в консоль воркера: "RabbitMQ отримав повідомлення!". Викличте її через python manage.py shell.
🔹 Завдання 2: "А що, якщо..."
Змініть код таски так, щоб вона ділила число на нуль (1/0).
Запустіть її. Подивіться в логи Celery. Що ви бачите? Чи впав Django-сервер? (Спойлер: Ні, впала тільки задача. Django живий!).
🔹 Завдання 3: Машина часу
Використовуйте метод .apply_async.
Завдання: зробіть так, щоб лист (або print) спрацював не одразу, а рівно через 1 хвилину після виклику.
Підказка: countdown=60.
🔹 Завдання 4: Міні-кейс "Зміна розміру аватарки"
Користувач завантажує фото 4000x4000 пікселів.
Напишіть (псевдо)код таски, яка:
1. Приймає image_path.
2. Відкриває зображення (бібліотека Pillow).
3. Зменшує його до 200x200.
4. Зберігає нову версію.
🔹 Завдання 5: Перевірка на міцність
Зупиніть Worker (Ctrl+C в терміналі Celery).
Відправте 10 задач з Django (task.delay()).
Запустіть Worker знову.
Питання: Задачі зникли чи виконалися всі одразу? (Це покаже вам надійність черги).
5. 💡 Мислення як у розробника
Як відрізнити новачка від Senior-розробника в цій темі?
-
"Fire and Forget" не завжди працює. Новачок думає: "Я відправив задачу і забув". Профі думає: "А що, якщо RabbitMQ впаде? А що, якщо зовнішній сервіс email недоступний?". Порада: Використовуйте
retry(повторні спроби). Якщо пошта не відправилась, Celery спробує ще раз через 5 хвилин. -
Атомарність. Новачок запускає таску до того, як транзакція в БД завершилась. Ситуація: Ви відправили таску "Обробити замовлення №5", але
commitу базу ще не пройшов. Worker намагається дістати замовлення №5, а його ще немає. Рішення:transaction.on_commit(lambda: my_task.delay()). -
Моніторинг. Ви не бачите, що відбувається в чергах очима. Інструмент: Встановіть Flower. Це веб-інтерфейс, де ви бачите графіки: скільки задач пройшло, скільки впало, як швидко вони виконуються.
6. 🧩 Підсумок
Отже, що ми сьогодні зробили? Ми перетворили наш Django-додаток з "однорукого касира", який робить все повільно і по черзі, на масштабовану фабрику.
Тепер ви вмієте: * ✅ Розвантажувати основний потік виконання. * ✅ Користуватися RabbitMQ як посередником. * ✅ Правильно передавати дані у фонові задачі (IDs, not Objects!).
Що далі? Зараз наші воркери просто чекають задач. Але що, якщо нам треба відправляти дайджест новин кожного понеділка о 9:00? На наступному уроці ми познайомимося з Celery Beat — будильником для ваших задач.
А поки — код сам себе не напише. Успіхів! 💻🚀