Ось готовий урок, створений за твоїм майстер-промптом.
🎓 Урок: Celery та Redis. Асинхронність у реальному житті
Привіт, друзі! 👋 Радий бачити вас на сьогоднішньому занятті.
Сьогодні ми розберемо тему, яка перетворює "гальмівні" веб-сайти на швидкі та чуйні додатки. Ми поговоримо про Celery та Redis.
1. 🔥 Вступ: Проблема "Втомленого Офіціанта"
Уявіть собі ситуацію. Ви заходите на сайт інтернет-магазину, натискаєте кнопку «Купити», і... сайт завмирає. Крутиться коліщатко завантаження. 3 секунди, 5 секунд, 10 секунд... Ви вже думаєте, що інтернет зник. Аж раптом — бац! — "Ваше замовлення прийнято, лист надіслано".
Що сталося? Чому ви чекали?
Справа в тому, що сервер у цей момент робив усе послідовно: 1. Зберіг замовлення в базу. 2. Почав формувати PDF-чек. 3. З'єднувався з поштовим сервером (який, можливо, тупив). 4. Відправляв email. 5. І тільки тоді відповів вам: "Все ок".
Риторичне питання: Чи хотіли б ви чекати в ресторані, поки офіціант піде на кухню, сам приготує страву, помиє посуд, і тільки потім повернеться до вас, щоб прийняти замовлення у наступного клієнта?
Звісно, ні! Це неефективно.
Як це має працювати: Офіціант (ваш веб-сервер) приймає замовлення, записує його на папірець, вішає на спеціальну дошку на кухні і миттєво повертається до вас. А кухарі (фонові процеси) вже готують страву, коли мають час.
У світі Python: * Офіціант — це ваш веб-фреймворк (Django, Flask, FastAPI). * Дошка із замовленнями — це Redis. * Кухар — це Celery.
Без цієї парочки будь-яка важка операція (обробка відео, надсилання розсилок, складні звіти) "повісить" ваш сайт.
2. 🧠 Теоретична база: Що відбувається "під капотом"?
Давайте розберемося з визначеннями, але без зайвої академічності.
Хто є хто?
- Celery (Селера 🌿) — це Task Queue (черга задач). Це бібліотека, яка дозволяє запускати код окремо від основного потоку програми. Це ваші "робітники" (workers), які постійно перевіряють, чи є для них робота.
- Redis — це Message Broker (посередник повідомлень). Celery не може спілкуватися з вашим сайтом напряму телепатично. Їм потрібне місце, куди сайт покладе задачу, а Celery її звідти забере. Redis — це надшвидка база даних (в оперативній пам'яті), яка ідеально підходить для ролі цієї "поштової скриньки".
Як це працює (Логіка процесу):
Уявіть цей процес покроково:
- Producer (Ваш сайт): Користувач натискає кнопку. Сайт каже: "Треба надіслати email". Але замість того, щоб слати його самому, він формує повідомлення ("Надішли лист юзеру ID 5") і кидає його в Redis. Сайт одразу відповідає юзеру: "Готово!". Час: 0.01 с.
- Broker (Redis): Тримає це повідомлення у списку (черзі).
- Consumer (Celery Worker): Це окремий процес, який запущено на сервері. Він постійно "смикає" Redis: "Є щось нове? Є щось нове?". Як тільки бачить задачу — хапає її, виконує (справді шле email) і записує результат.
📌 Що треба запам'ятати залізно:
- Веб-сервер НЕ виконує задачу, він лише планує її (створює таск).
- Celery і ваш сайт — це різні процеси. Якщо сайт впаде, Celery може продовжувати працювати (і навпаки).
3. 🧪 Приклади (Coding time!)
Для початку переконайтеся, що у вас встановлені Redis (сервер) та бібліотеки:
pip install celery redis
Приклад 1: Мінімальний (Hello World)
Створимо файл tasks.py. Це наша кухня.
from celery import Celery
import time
# Налаштовуємо Celery
# Перший аргумент - ім'я модуля
# broker - адреса нашого "посередника" (Redis)
app = Celery('hello', broker='redis://localhost:6379/0')
@app.task
def say_hello(name):
print(f"👋 Починаю вітатися з {name}...")
time.sleep(5) # Імітуємо важку роботу (наприклад, генерація звіту)
print(f"✅ Привіт, {name}!")
return "Done"
Тепер уявімо, що ми — веб-сайт (відкрийте термінал Python або окремий файл main.py).
❓ Питання до студента: Як ви думаєте, якщо я зараз запущу функцію say_hello("Alex"), вона виконається миттєво чи через 5 секунд?
Відповідь: Якщо викликати як звичайну функцію — через 5 секунд. Ми заблокуємо програму.
Але магія Celery — у методі .delay().
from tasks import say_hello
# Це відпрацює миттєво!
result = say_hello.delay("Alex")
print(f"Задачу відправлено! ID задачі: {result.id}")
Але стривайте! Ви не побачите повідомлення "Привіт, Alex" у терміналі, де запустили скрипт. Чому? Бо "кухар" ще не вийшов на зміну! Задача лежить у Redis і чекає.
Потрібно запустити Worker (у терміналі):
celery -A tasks worker --loglevel=INFO
Ось тепер ви побачите у логах воркера, як він підхопив задачу і виконав її.
Приклад 2: Реальна ситуація (Email)
У реальному житті ми не просто друкуємо текст.
@app.task
def send_confirmation_email(user_email):
# Тут могла б бути логіка SMTP
import time
print(f"📨 Підготовка листа для {user_email}...")
time.sleep(3) # Імітація з'єднання з сервером
print(f"📤 Лист надіслано на {user_email}")
return True
У вашому веб-контролері (наприклад, у Flask) це виглядало б так:
@app.route('/register', methods=['POST'])
def register():
email = request.form['email']
save_user_to_db(email)
# Асинхронний виклик! Користувач не чекає 3 секунди.
send_confirmation_email.delay(email)
return "Реєстрація успішна! Перевірте пошту."
4. 🛠 Практична частина
Час забруднити руки! Виконайте наступні завдання:
- 🔹 Запуск бази: Встановіть та запустіть Redis (або через Docker:
docker run -d -p 6379:6379 redis). - 🔹 Перший запуск: Створіть файл
tasks.pyз прикладу вище. Запустіть воркер в одному терміналі, а в іншому викличте задачу черезpython -c "from tasks import say_hello; say_hello.delay('Student')" - 🔹 Змініть умови: Додайте функцію
add(x, y), яка додає два числа, але з затримкою в 10 секунд. Запустіть 5 таких задач підряд у циклі. Подивіться, як воркер їх обробляє (послідовно чи паралельно? Підказка: Celery за замовчуванням запускає стільки процесів, скільки ядер у вашого процесора). - 🔹 Error Handling: Зробіть задачу, яка спеціально ділить на нуль. Подивіться, що напише воркер у логах.
- 🔹 Міні-кейс: Уявіть, що ви робите Instagram. Напишіть задачу
process_image(image_path), яка "імітує" накладання фільтру (простоsleepіprint). Викличте її.
Питання "А що, якщо...":
Що станеться, якщо ви вимкнете воркер (Ctrl+C), відправите 10 задач через .delay(), а потім знову увімкнете воркер?
(Спробуйте! Спойлер: Redis збереже їх, і воркер виконає їх, як тільки прокинеться. Нічого не загубиться!)
5. 💡 Мислення як у розробника
Ви тепер знаєте синтаксис, але як не наробити помилок? Ось як думають сеньйори:
-
Не передавайте об'єкти бази даних у Celery!
- Помилка:
send_email.delay(user_object) - Чому: Поки задача дійде до воркера, дані користувача в базі могли змінитися. Або серіалізація (перетворення об'єкта в текст для Redis) може зламатися.
- Правильно:
send_email.delay(user_id). Воркер отримає ID, сам піде в базу і дістане свіжі дані.
- Помилка:
-
Задачі мають бути ідемпотентними. Це складне слово означає просту річ: якщо виконати задачу двічі, нічого поганого не має статися.
- Приклад: Якщо задача "Списати гроші" виконається двічі через збій мережі — це біда. Потрібно перевіряти всередині задачі: "Чи вже списано гроші за цією транзакцією?".
-
Моніторинг. Коли задач тисячі, дивитися в консоль неможливо. Професіонали використовують Flower — це веб-інтерфейс для Celery. Він показує графіки, помилки та швидкість виконання.
6. 🧩 Підсумок
Отже, що ми сьогодні зробили? Ми навчилися делегувати. Ми розділили наш додаток на дві частини: 1. Швидкий фронт-офіс (веб-сайт), який миттєво відповідає клієнтам. 2. Потужний бек-офіс (Celery + Redis), який розгрібає важку роботу у фоні.
Тепер ви вмієте: * Налаштовувати зв'язку Celery + Redis. * Створювати фонові задачі. * Розуміти, як не змушувати користувача чекати.
👉 Тизер наступного уроку: А що, якщо задачу треба виконати не просто "у фоні", а чітко щопонеділка о 9:00? Або кожні 5 хвилин? На наступному уроці ми познайомимося з Celery Beat — планувальником, який перетворить ваш код на швейцарський годинник.
До зустрічі! 🚀